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;
54 const char *StrBufNOTNULL = ((char*) NULL) - 1;
56 const char HexList[256][3] = {
57 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
58 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
59 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
60 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
61 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
62 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
63 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
64 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
65 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
66 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
67 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
68 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
69 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
70 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
71 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
72 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
75 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
76 * StrBuf is a versatile class, aiding the handling of dynamic strings
77 * * reduce de/reallocations
78 * * reduce the need to remeasure it
79 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
80 * * allow asyncroneous IO for line and Blob based operations
81 * * reduce the use of memove in those
82 * * Quick filling in several operations with append functions
86 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
91 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
93 * use these operators to interfere with code demanding char*;
94 * if you need to own the content, smash me. Avoid, since we loose the length information.
98 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
100 * operations to get your Strings into a StrBuf, manipulating them, or appending
103 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
105 * Quick tokenizer; demands of the user to pull its tokens in sequence
109 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
111 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
115 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
117 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
118 * External Cursor to maintain the current read position inside of the buffer
119 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
123 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
129 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
131 * these functions translate the content of a buffer into another representation;
132 * some are combined Fillers and encoders
136 * Private Structure for the Stringbuffer
139 char *buf; /**< the pointer to the dynamic buffer */
140 long BufSize; /**< how many spcae do we optain */
141 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
142 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
144 long nIncreases; /**< for profiling; cound how many times we needed more */
145 char bt [SIZ]; /**< Stacktrace of last increase */
146 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
151 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
152 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
155 #ifdef HAVE_BACKTRACE
156 static void StrBufBacktrace(StrBuf *Buf, int which)
160 void *stack_frames[50];
165 pstart = pch = Buf->bt;
167 pstart = pch = Buf->bt_lastinc;
168 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
169 strings = backtrace_symbols(stack_frames, size);
170 for (i = 0; i < size; i++) {
172 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
174 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
183 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
185 if (hFreeDbglog == -1){
186 pid_t pid = getpid();
188 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
189 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
191 if ((*FreeMe)->nIncreases > 0)
195 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
197 (*FreeMe)->nIncreases,
201 (*FreeMe)->bt_lastinc);
202 n = write(hFreeDbglog, buf, n);
208 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
212 n = write(hFreeDbglog, buf, n);
216 void dbg_IncreaseBuf(StrBuf *IncMe)
219 #ifdef HAVE_BACKTRACE
220 StrBufBacktrace(Buf, 1);
224 void dbg_Init(StrBuf *Buf)
228 Buf->bt_lastinc[0] = '\0';
229 #ifdef HAVE_BACKTRACE
230 StrBufBacktrace(Buf, 0);
236 #define dbg_FreeStrBuf(a, b)
237 #define dbg_IncreaseBuf(a)
244 * @brief swaps the contents of two StrBufs
245 * this is to be used to have cheap switched between a work-buffer and a target buffer
247 * @param B second one
249 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
253 memcpy(&C, A, sizeof(*A));
254 memcpy(A, B, sizeof(*B));
255 memcpy(B, &C, sizeof(C));
260 * @ingroup StrBuf_Cast
261 * @brief Cast operator to Plain String
262 * @note if the buffer is altered by StrBuf operations, this pointer may become
263 * invalid. So don't lean on it after altering the buffer!
264 * Since this operation is considered cheap, rather call it often than risking
265 * your pointer to become invalid!
266 * @param Str the string we want to get the c-string representation for
267 * @returns the Pointer to the Content. Don't mess with it!
269 inline const char *ChrPtr(const StrBuf *Str)
277 * @ingroup StrBuf_Cast
278 * @brief since we know strlen()'s result, provide it here.
279 * @param Str the string to return the length to
280 * @returns contentlength of the buffer
282 inline int StrLength(const StrBuf *Str)
284 return (Str != NULL) ? Str->BufUsed : 0;
288 * @ingroup StrBuf_DeConstructors
289 * @brief local utility function to resize the buffer
290 * @param Buf the buffer whichs storage we should increase
291 * @param KeepOriginal should we copy the original buffer or just start over with a new one
292 * @param DestSize what should fit in after?
294 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
297 size_t NewSize = Buf->BufSize * 2;
303 while ((NewSize <= DestSize) && (NewSize != 0))
309 NewBuf= (char*) malloc(NewSize);
313 if (KeepOriginal && (Buf->BufUsed > 0))
315 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
324 Buf->BufSize = NewSize;
326 dbg_IncreaseBuf(Buf);
332 * @ingroup StrBuf_DeConstructors
333 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
334 * @param Buf Buffer to shrink (has to be empty)
335 * @param ThreshHold if the buffer is bigger then this, its readjusted
336 * @param NewSize if we Shrink it, how big are we going to be afterwards?
338 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
341 (Buf->BufUsed == 0) &&
342 (Buf->BufSize < ThreshHold)) {
344 Buf->buf = (char*) malloc(NewSize);
346 Buf->BufSize = NewSize;
351 * @ingroup StrBuf_DeConstructors
352 * @brief shrink long term buffers to their real size so they don't waste memory
353 * @param Buf buffer to shrink
354 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
355 * @returns physical size of the buffer
357 long StrBufShrinkToFit(StrBuf *Buf, int Force)
362 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
364 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
365 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
366 Buf->BufSize = Buf->BufUsed + 1;
374 * @ingroup StrBuf_DeConstructors
375 * @brief Allocate a new buffer with default buffer size
376 * @returns the new stringbuffer
378 StrBuf* NewStrBuf(void)
382 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
383 NewBuf->buf = (char*) malloc(BaseStrBufSize);
384 NewBuf->buf[0] = '\0';
385 NewBuf->BufSize = BaseStrBufSize;
387 NewBuf->ConstBuf = 0;
395 * @ingroup StrBuf_DeConstructors
396 * @brief Copy Constructor; returns a duplicate of CopyMe
397 * @param CopyMe Buffer to faxmilate
398 * @returns the new stringbuffer
400 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
407 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
408 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
409 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
410 NewBuf->BufUsed = CopyMe->BufUsed;
411 NewBuf->BufSize = CopyMe->BufSize;
412 NewBuf->ConstBuf = 0;
420 * @ingroup StrBuf_DeConstructors
421 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
422 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
423 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
424 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
425 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
426 * @returns the new stringbuffer
428 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
432 if (CreateRelpaceMe == NULL)
437 if (*CreateRelpaceMe != NULL)
438 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
440 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
444 if (CopyFlushMe == NULL)
446 if (*CreateRelpaceMe != NULL)
447 FlushStrBuf(*CreateRelpaceMe);
449 *CreateRelpaceMe = NewStrBuf();
454 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
455 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
456 * be a big IO-Buffer...
458 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
460 if (*CreateRelpaceMe == NULL)
462 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
467 NewBuf = *CreateRelpaceMe;
470 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
474 if (*CreateRelpaceMe == NULL)
476 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
480 NewBuf = *CreateRelpaceMe;
481 SwapBuffers (NewBuf, CopyFlushMe);
484 FlushStrBuf(CopyFlushMe);
489 * @ingroup StrBuf_DeConstructors
490 * @brief create a new Buffer using an existing c-string
491 * this function should also be used if you want to pre-suggest
492 * the buffer size to allocate in conjunction with ptr == NULL
493 * @param ptr the c-string to copy; may be NULL to create a blank instance
494 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
495 * @returns the new stringbuffer
497 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
500 size_t Siz = BaseStrBufSize;
503 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
505 CopySize = strlen((ptr != NULL)?ptr:"");
509 while ((Siz <= CopySize) && (Siz != 0))
517 NewBuf->buf = (char*) malloc(Siz);
518 if (NewBuf->buf == NULL)
523 NewBuf->BufSize = Siz;
525 memcpy(NewBuf->buf, ptr, CopySize);
526 NewBuf->buf[CopySize] = '\0';
527 NewBuf->BufUsed = CopySize;
530 NewBuf->buf[0] = '\0';
533 NewBuf->ConstBuf = 0;
541 * @ingroup StrBuf_DeConstructors
542 * @brief Set an existing buffer from a c-string
543 * @param Buf buffer to load
544 * @param ptr c-string to put into
545 * @param nChars set to -1 if we should work 0-terminated
546 * @returns the new length of the string
548 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
563 CopySize = strlen(ptr);
567 while ((Siz <= CopySize) && (Siz != 0))
575 if (Siz != Buf->BufSize)
576 IncreaseBuf(Buf, 0, Siz);
577 memcpy(Buf->buf, ptr, CopySize);
578 Buf->buf[CopySize] = '\0';
579 Buf->BufUsed = CopySize;
586 * @ingroup StrBuf_DeConstructors
587 * @brief use strbuf as wrapper for a string constant for easy handling
588 * @param StringConstant a string to wrap
589 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
591 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
595 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
596 NewBuf->buf = (char*) StringConstant;
597 NewBuf->BufSize = SizeOfStrConstant;
598 NewBuf->BufUsed = SizeOfStrConstant;
599 NewBuf->ConstBuf = 1;
608 * @ingroup StrBuf_DeConstructors
609 * @brief flush the content of a Buf; keep its struct
610 * @param buf Buffer to flush
612 int FlushStrBuf(StrBuf *buf)
614 if ((buf == NULL) || (buf->buf == NULL))
624 * @ingroup StrBuf_DeConstructors
625 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
626 * @param buf Buffer to wipe
628 int FLUSHStrBuf(StrBuf *buf)
634 if (buf->BufUsed > 0) {
635 memset(buf->buf, 0, buf->BufUsed);
642 int hFreeDbglog = -1;
645 * @ingroup StrBuf_DeConstructors
646 * @brief Release a Buffer
647 * Its a double pointer, so it can NULL your pointer
648 * so fancy SIG11 appear instead of random results
649 * @param FreeMe Pointer Pointer to the buffer to free
651 void FreeStrBuf (StrBuf **FreeMe)
656 dbg_FreeStrBuf(FreeMe, 'F');
658 if (!(*FreeMe)->ConstBuf)
659 free((*FreeMe)->buf);
665 * @ingroup StrBuf_DeConstructors
666 * @brief flatten a Buffer to the Char * we return
667 * Its a double pointer, so it can NULL your pointer
668 * so fancy SIG11 appear instead of random results
669 * The Callee then owns the buffer and is responsible for freeing it.
670 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
671 * @returns the pointer of the buffer; Callee owns the memory thereafter.
673 char *SmashStrBuf (StrBuf **SmashMe)
677 if ((SmashMe == NULL) || (*SmashMe == NULL))
680 dbg_FreeStrBuf(SmashMe, 'S');
682 Ret = (*SmashMe)->buf;
689 * @ingroup StrBuf_DeConstructors
690 * @brief Release the buffer
691 * If you want put your StrBuf into a Hash, use this as Destructor.
692 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
694 void HFreeStrBuf (void *VFreeMe)
696 StrBuf *FreeMe = (StrBuf*)VFreeMe;
700 dbg_FreeStrBuf(SmashMe, 'H');
702 if (!FreeMe->ConstBuf)
708 /*******************************************************************************
709 * Simple string transformations *
710 *******************************************************************************/
714 * @brief Wrapper around atol
716 long StrTol(const StrBuf *Buf)
721 return atol(Buf->buf);
728 * @brief Wrapper around atoi
730 int StrToi(const StrBuf *Buf)
734 if (Buf->BufUsed > 0)
735 return atoi(Buf->buf);
742 * @brief Checks to see if the string is a pure number
743 * @param Buf The buffer to inspect
744 * @returns 1 if its a pure number, 0, if not.
746 int StrBufIsNumber(const StrBuf *Buf) {
748 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
751 strtoll(Buf->buf, &pEnd, 10);
752 if (pEnd == Buf->buf)
754 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
756 if (Buf->buf == pEnd)
762 * @ingroup StrBuf_Filler
763 * @brief modifies a Single char of the Buf
764 * You can point to it via char* or a zero-based integer
765 * @param Buf The buffer to manipulate
766 * @param ptr char* to zero; use NULL if unused
767 * @param nThChar zero based pointer into the string; use -1 if unused
768 * @param PeekValue The Character to place into the position
770 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
775 nThChar = ptr - Buf->buf;
776 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
778 Buf->buf[nThChar] = PeekValue;
783 * @ingroup StrBuf_Filler
784 * @brief modifies a range of chars of the Buf
785 * You can point to it via char* or a zero-based integer
786 * @param Buf The buffer to manipulate
787 * @param ptr char* to zero; use NULL if unused
788 * @param nThChar zero based pointer into the string; use -1 if unused
789 * @param nChars how many chars are to be flushed?
790 * @param PookValue The Character to place into that area
792 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
797 nThChar = ptr - Buf->buf;
798 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
800 if (nThChar + nChars > Buf->BufUsed)
801 nChars = Buf->BufUsed - nThChar;
803 memset(Buf->buf + nThChar, PookValue, nChars);
804 /* just to be shure... */
805 Buf->buf[Buf->BufUsed] = 0;
810 * @ingroup StrBuf_Filler
811 * @brief Append a StringBuffer to the buffer
812 * @param Buf Buffer to modify
813 * @param AppendBuf Buffer to copy at the end of our buffer
814 * @param Offset Should we start copying from an offset?
816 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
818 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
821 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
824 AppendBuf->BufUsed + Buf->BufUsed);
826 memcpy(Buf->buf + Buf->BufUsed,
827 AppendBuf->buf + Offset,
828 AppendBuf->BufUsed - Offset);
829 Buf->BufUsed += AppendBuf->BufUsed - Offset;
830 Buf->buf[Buf->BufUsed] = '\0';
835 * @ingroup StrBuf_Filler
836 * @brief Append a C-String to the buffer
837 * @param Buf Buffer to modify
838 * @param AppendBuf Buffer to copy at the end of our buffer
839 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
840 * @param Offset Should we start copying from an offset?
842 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
845 long BufSizeRequired;
847 if ((AppendBuf == NULL) || (Buf == NULL))
851 aps = strlen(AppendBuf + Offset);
853 aps = AppendSize - Offset;
855 BufSizeRequired = Buf->BufUsed + aps + 1;
856 if (Buf->BufSize <= BufSizeRequired)
857 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
859 memcpy(Buf->buf + Buf->BufUsed,
863 Buf->buf[Buf->BufUsed] = '\0';
867 * @ingroup StrBuf_Filler
868 * @brief sprintf like function appending the formated string to the buffer
869 * vsnprintf version to wrap into own calls
870 * @param Buf Buffer to extend by format and Params
871 * @param format printf alike format to add
872 * @param ap va_list containing the items for format
874 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
882 if ((Buf == NULL) || (format == NULL))
885 BufSize = Buf->BufSize;
886 nWritten = Buf->BufSize + 1;
887 Offset = Buf->BufUsed;
888 newused = Offset + nWritten;
890 while (newused >= BufSize) {
892 nWritten = vsnprintf(Buf->buf + Offset,
893 Buf->BufSize - Offset,
896 newused = Offset + nWritten;
897 if (newused >= Buf->BufSize) {
898 if (IncreaseBuf(Buf, 1, newused) == -1)
899 return; /* TODO: error handling? */
900 newused = Buf->BufSize + 1;
903 Buf->BufUsed = Offset + nWritten;
904 BufSize = Buf->BufSize;
911 * @ingroup StrBuf_Filler
912 * @brief sprintf like function appending the formated string to the buffer
913 * @param Buf Buffer to extend by format and Params
914 * @param format printf alike format to add
916 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
924 if ((Buf == NULL) || (format == NULL))
927 BufSize = Buf->BufSize;
928 nWritten = Buf->BufSize + 1;
929 Offset = Buf->BufUsed;
930 newused = Offset + nWritten;
932 while (newused >= BufSize) {
933 va_start(arg_ptr, format);
934 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
935 Buf->BufSize - Buf->BufUsed,
938 newused = Buf->BufUsed + nWritten;
939 if (newused >= Buf->BufSize) {
940 if (IncreaseBuf(Buf, 1, newused) == -1)
941 return; /* TODO: error handling? */
942 newused = Buf->BufSize + 1;
945 Buf->BufUsed += nWritten;
946 BufSize = Buf->BufSize;
953 * @ingroup StrBuf_Filler
954 * @brief sprintf like function putting the formated string into the buffer
955 * @param Buf Buffer to extend by format and Parameters
956 * @param format printf alike format to add
958 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
963 if ((Buf == NULL) || (format == NULL))
966 nWritten = Buf->BufSize + 1;
967 while (nWritten >= Buf->BufSize) {
968 va_start(arg_ptr, format);
969 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
971 if (nWritten >= Buf->BufSize) {
972 if (IncreaseBuf(Buf, 0, 0) == -1)
973 return; /* TODO: error handling? */
974 nWritten = Buf->BufSize + 1;
977 Buf->BufUsed = nWritten ;
982 * @ingroup StrBuf_Filler
983 * @brief Callback for cURL to append the webserver reply to a buffer
984 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
985 * @param size pre-defined by the cURL API; see man 3 curl for mre info
986 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
987 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
989 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
998 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1005 * @brief extracts a substring from Source into dest
1006 * @param dest buffer to place substring into
1007 * @param Source string to copy substring from
1008 * @param Offset chars to skip from start
1009 * @param nChars number of chars to copy
1010 * @returns the number of chars copied; may be different from nChars due to the size of Source
1012 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1014 size_t NCharsRemain;
1015 if (Offset > Source->BufUsed)
1021 if (Offset + nChars < Source->BufUsed)
1023 if (nChars >= dest->BufSize)
1024 IncreaseBuf(dest, 0, nChars + 1);
1025 memcpy(dest->buf, Source->buf + Offset, nChars);
1026 dest->BufUsed = nChars;
1027 dest->buf[dest->BufUsed] = '\0';
1030 NCharsRemain = Source->BufUsed - Offset;
1031 if (NCharsRemain >= dest->BufSize)
1032 IncreaseBuf(dest, 0, NCharsRemain + 1);
1033 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1034 dest->BufUsed = NCharsRemain;
1035 dest->buf[dest->BufUsed] = '\0';
1036 return NCharsRemain;
1041 * @brief Cut nChars from the start of the string
1042 * @param Buf Buffer to modify
1043 * @param nChars how many chars should be skipped?
1045 void StrBufCutLeft(StrBuf *Buf, int nChars)
1047 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1048 if (nChars >= Buf->BufUsed) {
1052 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1053 Buf->BufUsed -= nChars;
1054 Buf->buf[Buf->BufUsed] = '\0';
1059 * @brief Cut the trailing n Chars from the string
1060 * @param Buf Buffer to modify
1061 * @param nChars how many chars should be trunkated?
1063 void StrBufCutRight(StrBuf *Buf, int nChars)
1065 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1066 if (nChars >= Buf->BufUsed) {
1070 Buf->BufUsed -= nChars;
1071 Buf->buf[Buf->BufUsed] = '\0';
1076 * @brief Cut the string after n Chars
1077 * @param Buf Buffer to modify
1078 * @param AfternChars after how many chars should we trunkate the string?
1079 * @param At if non-null and points inside of our string, cut it there.
1081 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1083 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1085 AfternChars = At - Buf->buf;
1088 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1090 Buf->BufUsed = AfternChars;
1091 Buf->buf[Buf->BufUsed] = '\0';
1097 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1098 * @param Buf the string to modify
1100 void StrBufTrim(StrBuf *Buf)
1103 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1105 while ((Buf->BufUsed > 0) &&
1106 isspace(Buf->buf[Buf->BufUsed - 1]))
1110 Buf->buf[Buf->BufUsed] = '\0';
1112 if (Buf->BufUsed == 0) return;
1114 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1117 if (delta > 0) StrBufCutLeft(Buf, delta);
1121 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1122 * @param Buf the string to modify
1124 void StrBufSpaceToBlank(StrBuf *Buf)
1128 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1131 pche = pch + Buf->BufUsed;
1140 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1146 if ((Buf == NULL) || (Buf->buf == NULL))
1148 pLeft = pBuff = Buf->buf;
1149 while (pBuff != NULL) {
1151 pBuff = strchr(pBuff, leftboundary);
1160 pRight = strchr(pBuff, rightboundary);
1162 StrBufCutAt(Buf, 0, pRight);
1164 StrBufCutLeft(Buf, pLeft - Buf->buf);
1169 * @ingroup StrBuf_Filler
1170 * @brief uppercase the contents of a buffer
1171 * @param Buf the buffer to translate
1173 void StrBufUpCase(StrBuf *Buf)
1177 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1180 pche = pch + Buf->BufUsed;
1181 while (pch < pche) {
1182 *pch = toupper(*pch);
1189 * @ingroup StrBuf_Filler
1190 * @brief lowercase the contents of a buffer
1191 * @param Buf the buffer to translate
1193 void StrBufLowerCase(StrBuf *Buf)
1197 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1200 pche = pch + Buf->BufUsed;
1201 while (pch < pche) {
1202 *pch = tolower(*pch);
1208 /*******************************************************************************
1209 * a tokenizer that kills, maims, and destroys *
1210 *******************************************************************************/
1213 * @ingroup StrBuf_Tokenizer
1214 * @brief Replace a token at a given place with a given length by another token with given length
1215 * @param Buf String where to work on
1216 * @param where where inside of the Buf is the search-token
1217 * @param HowLong How long is the token to be replaced
1218 * @param Repl Token to insert at 'where'
1219 * @param ReplLen Length of repl
1220 * @returns -1 if fail else length of resulting Buf
1222 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1223 const char *Repl, long ReplLen)
1226 if ((Buf == NULL) ||
1227 (where > Buf->BufUsed) ||
1228 (where + HowLong > Buf->BufUsed))
1231 if (where + ReplLen - HowLong > Buf->BufSize)
1232 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1235 memmove(Buf->buf + where + ReplLen,
1236 Buf->buf + where + HowLong,
1237 Buf->BufUsed - where - HowLong);
1239 memcpy(Buf->buf + where,
1242 Buf->BufUsed += ReplLen - HowLong;
1244 return Buf->BufUsed;
1248 * @ingroup StrBuf_Tokenizer
1249 * @brief Counts the numbmer of tokens in a buffer
1250 * @param source String to count tokens in
1251 * @param tok Tokenizer char to count
1252 * @returns numbers of tokenizer chars found
1254 int StrBufNum_tokens(const StrBuf *source, char tok)
1258 if ((source == NULL) || (source->BufUsed == 0))
1260 if ((source->BufUsed == 1) && (*source->buf == tok))
1264 pche = pch + source->BufUsed;
1275 * @ingroup StrBuf_Tokenizer
1276 * @brief a string tokenizer
1277 * @param Source StringBuffer to read into
1278 * @param parmnum n'th Parameter to remove
1279 * @param separator tokenizer character
1280 * @returns -1 if not found, else length of token.
1282 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1285 char *d, *s, *end; /* dest, source */
1288 /* Find desired @parameter */
1289 end = Source->buf + Source->BufUsed;
1291 while ((d <= end) &&
1294 /* End of string, bail! */
1299 if (*d == separator) {
1304 if ((d == NULL) || (d >= end))
1305 return 0; /* @Parameter not found */
1307 /* Find next @parameter */
1309 while ((s <= end) &&
1310 (*s && *s != separator))
1314 if (*s == separator)
1318 /* Hack and slash */
1323 memmove(d, s, Source->BufUsed - (s - Source->buf));
1324 Source->BufUsed += ReducedBy;
1325 Source->buf[Source->BufUsed] = '\0';
1327 else if (d == Source->buf) {
1329 Source->BufUsed = 0;
1333 Source->BufUsed += ReducedBy;
1346 * @ingroup StrBuf_Tokenizer
1347 * @brief a string tokenizer
1348 * @param dest Destination StringBuffer
1349 * @param Source StringBuffer to read into
1350 * @param parmnum n'th Parameter to extract
1351 * @param separator tokenizer character
1352 * @returns -1 if not found, else length of token.
1354 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1356 const char *s, *e; //* source * /
1357 int len = 0; //* running total length of extracted string * /
1358 int current_token = 0; //* token currently being processed * /
1361 dest->buf[0] = '\0';
1367 if ((Source == NULL) || (Source->BufUsed ==0)) {
1371 e = s + Source->BufUsed;
1374 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1376 while ((s < e) && !IsEmptyStr(s)) {
1377 if (*s == separator) {
1380 if (len >= dest->BufSize) {
1381 dest->BufUsed = len;
1382 if (IncreaseBuf(dest, 1, -1) < 0) {
1387 if ( (current_token == parmnum) &&
1388 (*s != separator)) {
1389 dest->buf[len] = *s;
1392 else if (current_token > parmnum) {
1398 dest->buf[len] = '\0';
1399 dest->BufUsed = len;
1401 if (current_token < parmnum) {
1402 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1405 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1414 * @ingroup StrBuf_Tokenizer
1415 * @brief a string tokenizer to fetch an integer
1416 * @param Source String containing tokens
1417 * @param parmnum n'th Parameter to extract
1418 * @param separator tokenizer character
1419 * @returns 0 if not found, else integer representation of the token
1421 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1431 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1438 * @ingroup StrBuf_Tokenizer
1439 * @brief a string tokenizer to fetch a long integer
1440 * @param Source String containing tokens
1441 * @param parmnum n'th Parameter to extract
1442 * @param separator tokenizer character
1443 * @returns 0 if not found, else long integer representation of the token
1445 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1455 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1463 * @ingroup StrBuf_Tokenizer
1464 * @brief a string tokenizer to fetch an unsigned long
1465 * @param Source String containing tokens
1466 * @param parmnum n'th Parameter to extract
1467 * @param separator tokenizer character
1468 * @returns 0 if not found, else unsigned long representation of the token
1470 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1481 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1485 return (unsigned long) atol(pnum);
1494 * @ingroup StrBuf_NextTokenizer
1495 * @brief a string tokenizer; Bounds checker
1496 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1497 * @param Source our tokenbuffer
1498 * @param pStart the token iterator pointer to inspect
1499 * @returns whether the revolving pointer is inside of the search range
1501 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1503 if ((Source == NULL) ||
1504 (*pStart == StrBufNOTNULL) ||
1505 (Source->BufUsed == 0))
1509 if (*pStart == NULL)
1513 else if (*pStart > Source->buf + Source->BufUsed)
1517 else if (*pStart <= Source->buf)
1526 * @ingroup StrBuf_NextTokenizer
1527 * @brief a string tokenizer
1528 * @param dest Destination StringBuffer
1529 * @param Source StringBuffer to read into
1530 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1531 * @param separator tokenizer
1532 * @returns -1 if not found, else length of token.
1534 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1536 const char *s; /* source */
1537 const char *EndBuffer; /* end stop of source buffer */
1538 int current_token = 0; /* token currently being processed */
1539 int len = 0; /* running total length of extracted string */
1541 if ((Source == NULL) ||
1542 (Source->BufUsed == 0) )
1544 *pStart = StrBufNOTNULL;
1550 EndBuffer = Source->buf + Source->BufUsed;
1554 dest->buf[0] = '\0';
1559 *pStart = EndBuffer + 1;
1563 if (*pStart == NULL)
1565 *pStart = Source->buf; /* we're starting to examine this buffer. */
1567 else if ((*pStart < Source->buf) ||
1568 (*pStart > EndBuffer ) )
1570 return -1; /* no more tokens to find. */
1574 /* start to find the next token */
1575 while ((s <= EndBuffer) &&
1576 (current_token == 0) )
1578 if (*s == separator)
1580 /* we found the next token */
1584 if (len >= dest->BufSize)
1586 /* our Dest-buffer isn't big enough, increase it. */
1587 dest->BufUsed = len;
1589 if (IncreaseBuf(dest, 1, -1) < 0) {
1590 /* WHUT? no more mem? bail out. */
1597 if ( (current_token == 0 ) && /* are we in our target token? */
1598 (!IsEmptyStr(s) ) &&
1599 (separator != *s) ) /* don't copy the token itself */
1601 dest->buf[len] = *s; /* Copy the payload */
1602 ++len; /* remember the bigger size. */
1608 /* did we reach the end? */
1609 if ((s > EndBuffer)) {
1610 EndBuffer = StrBufNOTNULL;
1611 *pStart = EndBuffer;
1614 *pStart = s; /* remember the position for the next run */
1617 /* sanitize our extracted token */
1618 dest->buf[len] = '\0';
1619 dest->BufUsed = len;
1626 * @ingroup StrBuf_NextTokenizer
1627 * @brief a string tokenizer
1628 * @param Source StringBuffer to read from
1629 * @param pStart pointer to the end of the last token. Feed with NULL.
1630 * @param separator tokenizer character
1631 * @param nTokens number of tokens to fastforward over
1632 * @returns -1 if not found, else length of token.
1634 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1636 const char *s, *EndBuffer; //* source * /
1637 int len = 0; //* running total length of extracted string * /
1638 int current_token = 0; //* token currently being processed * /
1640 if ((Source == NULL) ||
1641 (Source->BufUsed ==0)) {
1645 return Source->BufUsed;
1647 if (*pStart == NULL)
1648 *pStart = Source->buf;
1650 EndBuffer = Source->buf + Source->BufUsed;
1652 if ((*pStart < Source->buf) ||
1653 (*pStart > EndBuffer)) {
1661 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1663 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1664 if (*s == separator) {
1667 if (current_token >= nTokens) {
1679 * @ingroup StrBuf_NextTokenizer
1680 * @brief a string tokenizer to fetch an integer
1681 * @param Source StringBuffer to read from
1682 * @param pStart Cursor on the tokenstring
1683 * @param separator tokenizer character
1684 * @returns 0 if not found, else integer representation of the token
1686 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1696 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1703 * @ingroup StrBuf_NextTokenizer
1704 * @brief a string tokenizer to fetch a long integer
1705 * @param Source StringBuffer to read from
1706 * @param pStart Cursor on the tokenstring
1707 * @param separator tokenizer character
1708 * @returns 0 if not found, else long integer representation of the token
1710 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1720 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1728 * @ingroup StrBuf_NextTokenizer
1729 * @brief a string tokenizer to fetch an unsigned long
1730 * @param Source StringBuffer to read from
1731 * @param pStart Cursor on the tokenstring
1732 * @param separator tokenizer character
1733 * @returns 0 if not found, else unsigned long representation of the token
1735 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1746 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1750 return (unsigned long) atol(pnum);
1760 /*******************************************************************************
1761 * Escape Appending *
1762 *******************************************************************************/
1765 * @ingroup StrBuf_DeEnCoder
1766 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1767 * @param OutBuf the output buffer
1768 * @param In Buffer to encode
1769 * @param PlainIn way in from plain old c strings
1771 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1773 const char *pch, *pche;
1777 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1779 if (PlainIn != NULL) {
1780 len = strlen(PlainIn);
1786 pche = pch + In->BufUsed;
1793 pt = OutBuf->buf + OutBuf->BufUsed;
1794 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1796 while (pch < pche) {
1798 IncreaseBuf(OutBuf, 1, -1);
1799 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1800 pt = OutBuf->buf + OutBuf->BufUsed;
1803 if((*pch >= 'a' && *pch <= 'z') ||
1804 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1805 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1806 (*pch == '!') || (*pch == '_') ||
1807 (*pch == ',') || (*pch == '.'))
1814 *(pt + 1) = HexList[(unsigned char)*pch][0];
1815 *(pt + 2) = HexList[(unsigned char)*pch][1];
1817 OutBuf->BufUsed += 3;
1825 * @ingroup StrBuf_DeEnCoder
1826 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1827 * @param OutBuf the output buffer
1828 * @param In Buffer to encode
1829 * @param PlainIn way in from plain old c strings
1831 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1833 const char *pch, *pche;
1837 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1839 if (PlainIn != NULL) {
1840 len = strlen(PlainIn);
1846 pche = pch + In->BufUsed;
1853 pt = OutBuf->buf + OutBuf->BufUsed;
1854 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1856 while (pch < pche) {
1858 IncreaseBuf(OutBuf, 1, -1);
1859 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1860 pt = OutBuf->buf + OutBuf->BufUsed;
1863 if((*pch >= 'a' && *pch <= 'z') ||
1864 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1865 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1866 (*pch == '!') || (*pch == '_') ||
1867 (*pch == ',') || (*pch == '.'))
1874 *(pt + 1) = HexList[(unsigned char)*pch][0];
1875 *(pt + 2) = HexList[(unsigned char)*pch][1];
1877 OutBuf->BufUsed += 3;
1885 * @ingroup StrBuf_DeEnCoder
1886 * @brief append a string in hex encoding to the buffer
1887 * @param OutBuf the output buffer
1888 * @param In Buffer to encode
1889 * @param PlainIn way in from plain old c strings
1890 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1892 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1894 const unsigned char *pch, *pche;
1898 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1900 if (PlainIn != NULL) {
1902 len = strlen((const char*)PlainIn);
1909 pch = (const unsigned char*)In->buf;
1910 pche = pch + In->BufUsed;
1917 pt = OutBuf->buf + OutBuf->BufUsed;
1918 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1920 while (pch < pche) {
1922 IncreaseBuf(OutBuf, 1, -1);
1923 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1924 pt = OutBuf->buf + OutBuf->BufUsed;
1927 *pt = HexList[*pch][0];
1929 *pt = HexList[*pch][1];
1930 pt ++; pch ++; OutBuf->BufUsed += 2;
1936 * @ingroup StrBuf_DeEnCoder
1937 * @brief append a string in hex encoding to the buffer
1938 * @param OutBuf the output buffer
1939 * @param In Buffer to encode
1940 * @param PlainIn way in from plain old c strings
1942 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1944 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1948 * @ingroup StrBuf_DeEnCoder
1949 * @brief Append a string, escaping characters which have meaning in HTML.
1951 * @param Target target buffer
1952 * @param Source source buffer; set to NULL if you just have a C-String
1953 * @param PlainIn Plain-C string to append; set to NULL if unused
1954 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1955 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1956 * if set to 2, linebreaks are replaced by <br/>
1958 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1960 const char *aptr, *eiptr;
1964 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1967 if (PlainIn != NULL) {
1969 len = strlen(PlainIn);
1974 eiptr = aptr + Source->BufUsed;
1975 len = Source->BufUsed;
1981 bptr = Target->buf + Target->BufUsed;
1982 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1984 while (aptr < eiptr){
1986 IncreaseBuf(Target, 1, -1);
1987 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1988 bptr = Target->buf + Target->BufUsed;
1991 memcpy(bptr, "<", 4);
1993 Target->BufUsed += 4;
1995 else if (*aptr == '>') {
1996 memcpy(bptr, ">", 4);
1998 Target->BufUsed += 4;
2000 else if (*aptr == '&') {
2001 memcpy(bptr, "&", 5);
2003 Target->BufUsed += 5;
2005 else if (*aptr == '"') {
2006 memcpy(bptr, """, 6);
2008 Target->BufUsed += 6;
2010 else if (*aptr == '\'') {
2011 memcpy(bptr, "'", 5);
2013 Target->BufUsed += 5;
2015 else if (*aptr == LB) {
2020 else if (*aptr == RB) {
2025 else if (*aptr == QU) {
2030 else if ((*aptr == 32) && (nbsp == 1)) {
2031 memcpy(bptr, " ", 6);
2033 Target->BufUsed += 6;
2035 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2036 *bptr='\0'; /* nothing */
2038 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2039 memcpy(bptr, "<br/>", 11);
2041 Target->BufUsed += 11;
2045 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2046 *bptr='\0'; /* nothing */
2056 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2058 return Target->BufUsed;
2062 * @ingroup StrBuf_DeEnCoder
2063 * @brief Append a string, escaping characters which have meaning in HTML.
2064 * Converts linebreaks into blanks; escapes single quotes
2065 * @param Target target buffer
2066 * @param Source source buffer; set to NULL if you just have a C-String
2067 * @param PlainIn Plain-C string to append; set to NULL if unused
2069 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2071 const char *aptr, *eiptr;
2075 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2078 if (PlainIn != NULL) {
2080 len = strlen(PlainIn);
2085 eiptr = aptr + Source->BufUsed;
2086 len = Source->BufUsed;
2092 eptr = Target->buf + Target->BufSize - 8;
2093 tptr = Target->buf + Target->BufUsed;
2095 while (aptr < eiptr){
2097 IncreaseBuf(Target, 1, -1);
2098 eptr = Target->buf + Target->BufSize - 8;
2099 tptr = Target->buf + Target->BufUsed;
2102 if (*aptr == '\n') {
2106 else if (*aptr == '\r') {
2110 else if (*aptr == '\'') {
2116 Target->BufUsed += 5;
2129 * @ingroup StrBuf_DeEnCoder
2130 * @brief Append a string, escaping characters which have meaning in ICAL.
2132 * @param Target target buffer
2133 * @param Source source buffer; set to NULL if you just have a C-String
2134 * @param PlainIn Plain-C string to append; set to NULL if unused
2136 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2138 const char *aptr, *eiptr;
2142 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2145 if (PlainIn != NULL) {
2147 len = strlen(PlainIn);
2152 eiptr = aptr + Source->BufUsed;
2153 len = Source->BufUsed;
2159 eptr = Target->buf + Target->BufSize - 8;
2160 tptr = Target->buf + Target->BufUsed;
2162 while (aptr < eiptr){
2163 if(tptr + 3 >= eptr) {
2164 IncreaseBuf(Target, 1, -1);
2165 eptr = Target->buf + Target->BufSize - 8;
2166 tptr = Target->buf + Target->BufUsed;
2169 if (*aptr == '\n') {
2176 else if (*aptr == '\r') {
2183 else if (*aptr == ',') {
2199 * @ingroup StrBuf_DeEnCoder
2200 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2202 * @param Target target buffer
2203 * @param Source source buffer; set to NULL if you just have a C-String
2204 * @param PlainIn Plain-C string to append; set to NULL if unused
2205 * @returns size of result or -1
2207 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2209 const char *aptr, *eiptr;
2214 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2217 if (PlainIn != NULL) {
2219 len = strlen(PlainIn);
2224 eiptr = aptr + Source->BufUsed;
2225 len = Source->BufUsed;
2231 bptr = Target->buf + Target->BufUsed;
2232 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2234 while (aptr < eiptr){
2236 IncreaseBuf(Target, 1, -1);
2237 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2238 bptr = Target->buf + Target->BufUsed;
2242 memcpy(bptr, HKEY("\\n"));
2244 Target->BufUsed += 2;
2247 memcpy(bptr, HKEY("\\r"));
2249 Target->BufUsed += 2;
2256 Target->BufUsed += 2;
2259 if ((*(aptr + 1) == 'u') &&
2260 isxdigit(*(aptr + 2)) &&
2261 isxdigit(*(aptr + 3)) &&
2262 isxdigit(*(aptr + 4)) &&
2263 isxdigit(*(aptr + 5)))
2264 { /* oh, a unicode escaper. let it pass through. */
2265 memcpy(bptr, aptr, 6);
2268 Target->BufUsed += 6;
2276 Target->BufUsed += 2;
2284 Target->BufUsed += 2;
2291 Target->BufUsed += 2;
2298 Target->BufUsed += 2;
2301 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2302 while (IsUtf8Sequence > 0){
2305 if (--IsUtf8Sequence)
2313 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2315 return Target->BufUsed;
2319 * @ingroup StrBuf_DeEnCoder
2320 * @brief Append a string, escaping characters which have meaning in HTML + json.
2322 * @param Target target buffer
2323 * @param Source source buffer; set to NULL if you just have a C-String
2324 * @param PlainIn Plain-C string to append; set to NULL if unused
2325 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2326 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2327 * if set to 2, linebreaks are replaced by <br/>
2329 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2331 const char *aptr, *eiptr;
2334 int IsUtf8Sequence = 0;
2336 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2339 if (PlainIn != NULL) {
2341 len = strlen(PlainIn);
2346 eiptr = aptr + Source->BufUsed;
2347 len = Source->BufUsed;
2353 bptr = Target->buf + Target->BufUsed;
2354 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2356 while (aptr < eiptr){
2358 IncreaseBuf(Target, 1, -1);
2359 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2360 bptr = Target->buf + Target->BufUsed;
2364 memcpy(bptr, HKEY("<"));
2366 Target->BufUsed += 4;
2369 memcpy(bptr, HKEY(">"));
2371 Target->BufUsed += 4;
2374 memcpy(bptr, HKEY("&"));
2376 Target->BufUsed += 5;
2389 switch (nolinebreaks) {
2391 *bptr='\0'; /* nothing */
2394 memcpy(bptr, HKEY("<br/>"));
2396 Target->BufUsed += 11;
2399 memcpy(bptr, HKEY("\\n"));
2401 Target->BufUsed += 2;
2405 switch (nolinebreaks) {
2408 *bptr='\0'; /* nothing */
2411 memcpy(bptr, HKEY("\\r"));
2413 Target->BufUsed += 2;
2423 Target->BufUsed += 2;
2426 if ((*(aptr + 1) == 'u') &&
2427 isxdigit(*(aptr + 2)) &&
2428 isxdigit(*(aptr + 3)) &&
2429 isxdigit(*(aptr + 4)) &&
2430 isxdigit(*(aptr + 5)))
2431 { /* oh, a unicode escaper. let it pass through. */
2432 memcpy(bptr, aptr, 6);
2435 Target->BufUsed += 6;
2443 Target->BufUsed += 2;
2451 Target->BufUsed += 2;
2458 Target->BufUsed += 2;
2465 Target->BufUsed += 2;
2469 memcpy(bptr, HKEY(" "));
2471 Target->BufUsed += 6;
2475 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2476 while (IsUtf8Sequence > 0){
2479 if (--IsUtf8Sequence)
2487 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2489 return Target->BufUsed;
2493 * @ingroup StrBuf_DeEnCoder
2494 * @brief unhide special chars hidden to the HTML escaper
2495 * @param target buffer to put the unescaped string in
2496 * @param source buffer to unescape
2498 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2504 FlushStrBuf(target);
2506 if (source == NULL ||target == NULL)
2511 len = source->BufUsed;
2512 for (a = 0; a < len; ++a) {
2513 if (target->BufUsed >= target->BufSize)
2514 IncreaseBuf(target, 1, -1);
2516 if (source->buf[a] == '=') {
2517 hex[0] = source->buf[a + 1];
2518 hex[1] = source->buf[a + 2];
2521 sscanf(hex, "%02x", &b);
2522 target->buf[target->BufUsed] = b;
2523 target->buf[++target->BufUsed] = 0;
2527 target->buf[target->BufUsed] = source->buf[a];
2528 target->buf[++target->BufUsed] = 0;
2535 * @ingroup StrBuf_DeEnCoder
2536 * @brief hide special chars from the HTML escapers and friends
2537 * @param target buffer to put the escaped string in
2538 * @param source buffer to escape
2540 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2545 FlushStrBuf(target);
2547 if (source == NULL ||target == NULL)
2552 len = source->BufUsed;
2553 for (i=0; i<len; ++i) {
2554 if (target->BufUsed + 4 >= target->BufSize)
2555 IncreaseBuf(target, 1, -1);
2556 if ( (isalnum(source->buf[i])) ||
2557 (source->buf[i]=='-') ||
2558 (source->buf[i]=='_') ) {
2559 target->buf[target->BufUsed++] = source->buf[i];
2562 sprintf(&target->buf[target->BufUsed],
2564 (0xFF &source->buf[i]));
2565 target->BufUsed += 3;
2568 target->buf[target->BufUsed + 1] = '\0';
2572 /*******************************************************************************
2573 * Quoted Printable de/encoding *
2574 *******************************************************************************/
2577 * @ingroup StrBuf_DeEnCoder
2578 * @brief decode a buffer from base 64 encoding; destroys original
2579 * @param Buf Buffor to transform
2581 int StrBufDecodeBase64(StrBuf *Buf)
2585 if (Buf == NULL) return -1;
2587 xferbuf = (char*) malloc(Buf->BufSize);
2589 siz = CtdlDecodeBase64(xferbuf,
2599 * @ingroup StrBuf_DeEnCoder
2600 * @brief decode a buffer from base 64 encoding; destroys original
2601 * @param Buf Buffor to transform
2603 int StrBufDecodeHex(StrBuf *Buf)
2606 char *pch, *pche, *pchi;
2608 if (Buf == NULL) return -1;
2610 pch = pchi = Buf->buf;
2611 pche = pch + Buf->BufUsed;
2613 while (pchi < pche){
2614 ch = decode_hex(pchi);
2621 Buf->BufUsed = pch - Buf->buf;
2622 return Buf->BufUsed;
2626 * @ingroup StrBuf_DeEnCoder
2627 * @brief replace all chars >0x20 && < 0x7F with Mute
2628 * @param Mute char to put over invalid chars
2629 * @param Buf Buffor to transform
2631 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2635 if (Buf == NULL) return -1;
2636 pch = (unsigned char *)Buf->buf;
2637 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2638 if ((*pch < 0x20) || (*pch > 0x7F))
2642 return Buf->BufUsed;
2647 * @ingroup StrBuf_DeEnCoder
2648 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2649 * @param Buf Buffer to translate
2650 * @param StripBlanks Reduce several blanks to one?
2652 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2661 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2662 Buf->buf[Buf->BufUsed - 1] = '\0';
2667 while (a < Buf->BufUsed) {
2668 if (Buf->buf[a] == '+')
2670 else if (Buf->buf[a] == '%') {
2671 /* don't let % chars through, rather truncate the input. */
2672 if (a + 2 > Buf->BufUsed) {
2677 hex[0] = Buf->buf[a + 1];
2678 hex[1] = Buf->buf[a + 2];
2681 sscanf(hex, "%02x", &b);
2682 Buf->buf[a] = (char) b;
2683 len = Buf->BufUsed - a - 2;
2685 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2697 * @ingroup StrBuf_DeEnCoder
2698 * @brief RFC2047-encode a header field if necessary.
2699 * If no non-ASCII characters are found, the string
2700 * will be copied verbatim without encoding.
2702 * @param target Target buffer.
2703 * @param source Source string to be encoded.
2704 * @returns encoded length; -1 if non success.
2706 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2708 const char headerStr[] = "=?UTF-8?Q?";
2709 int need_to_encode = 0;
2713 if ((source == NULL) ||
2717 while ((i < source->BufUsed) &&
2718 (!IsEmptyStr (&source->buf[i])) &&
2719 (need_to_encode == 0)) {
2720 if (((unsigned char) source->buf[i] < 32) ||
2721 ((unsigned char) source->buf[i] > 126)) {
2727 if (!need_to_encode) {
2728 if (*target == NULL) {
2729 *target = NewStrBufPlain(source->buf, source->BufUsed);
2732 FlushStrBuf(*target);
2733 StrBufAppendBuf(*target, source, 0);
2736 return (*target)->BufUsed;
2740 if (*target == NULL)
2741 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2742 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2743 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2744 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2745 (*target)->BufUsed = sizeof(headerStr) - 1;
2746 for (i=0; (i < source->BufUsed); ++i) {
2747 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2748 IncreaseBuf(*target, 1, 0);
2749 ch = (unsigned char) source->buf[i];
2759 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2760 (*target)->BufUsed += 3;
2764 (*target)->buf[(*target)->BufUsed] = '_';
2766 (*target)->buf[(*target)->BufUsed] = ch;
2767 (*target)->BufUsed++;
2771 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2772 IncreaseBuf(*target, 1, 0);
2774 (*target)->buf[(*target)->BufUsed++] = '?';
2775 (*target)->buf[(*target)->BufUsed++] = '=';
2776 (*target)->buf[(*target)->BufUsed] = '\0';
2777 return (*target)->BufUsed;;
2782 static void AddRecipient(StrBuf *Target,
2784 StrBuf *EmailAddress,
2789 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2790 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2792 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2793 StrBufRFC2047encode(&EncBuf, UserName);
2794 StrBufAppendBuf(Target, EncBuf, 0);
2795 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2796 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2798 if (StrLength(EmailAddress) > 0){
2799 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2800 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2801 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2807 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2808 * \param Recp Source list of email recipients
2809 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2810 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2811 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2812 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2814 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2816 StrBuf *EmailAddress,
2820 const char *pch, *pche;
2821 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2823 if ((Recp == NULL) || (StrLength(Recp) == 0))
2827 pche = pch + StrLength(Recp);
2829 if (!CheckEncode(pch, -1, pche))
2830 return NewStrBufDup(Recp);
2832 Target = NewStrBufPlain(NULL, StrLength(Recp));
2834 while ((pch != NULL) && (pch < pche))
2836 while (isspace(*pch)) pch++;
2837 UserEnd = EmailStart = EmailEnd = NULL;
2839 if ((*pch == '"') || (*pch == '\'')) {
2840 UserStart = pch + 1;
2842 UserEnd = strchr(UserStart, *pch);
2843 if (UserEnd == NULL)
2844 break; ///TODO: Userfeedback??
2845 EmailStart = UserEnd + 1;
2846 while (isspace(*EmailStart))
2848 if (UserEnd == UserStart) {
2849 UserStart = UserEnd = NULL;
2852 if (*EmailStart == '<') {
2854 EmailEnd = strchr(EmailStart, '>');
2855 if (EmailEnd == NULL)
2856 EmailEnd = strchr(EmailStart, ',');
2860 EmailEnd = strchr(EmailStart, ',');
2862 if (EmailEnd == NULL)
2869 EmailEnd = strchr(UserStart, ',');
2870 if (EmailEnd == NULL) {
2871 EmailEnd = strchr(pch, '>');
2873 if (EmailEnd != NULL) {
2883 while ((EmailEnd > UserStart) && !gt &&
2884 ((*EmailEnd == ',') ||
2885 (*EmailEnd == '>') ||
2886 (isspace(*EmailEnd))))
2888 if (*EmailEnd == '>')
2893 if (EmailEnd == UserStart)
2897 EmailStart = strchr(UserStart, '<');
2898 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2900 UserEnd = EmailStart;
2902 while ((UserEnd > UserStart) &&
2903 isspace (*(UserEnd - 1)))
2906 if (UserStart >= UserEnd)
2907 UserStart = UserEnd = NULL;
2909 else { /* this is a local recipient... no domain, just a realname */
2910 EmailStart = UserStart;
2911 At = strchr(EmailStart, '@');
2917 EmailStart = UserStart;
2923 if ((UserStart != NULL) && (UserEnd != NULL))
2924 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2925 else if ((UserStart != NULL) && (UserEnd == NULL))
2926 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2928 FlushStrBuf(UserName);
2930 if ((EmailStart != NULL) && (EmailEnd != NULL))
2931 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2932 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2933 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2935 FlushStrBuf(EmailAddress);
2937 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2942 if ((pch != NULL) && (*pch == ','))
2944 if (pch != NULL) while (isspace(*pch))
2953 * @brief replaces all occurances of 'search' by 'replace'
2954 * @param buf Buffer to modify
2955 * @param search character to search
2956 * @param replace character to replace search by
2958 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2963 for (i=0; i<buf->BufUsed; i++)
2964 if (buf->buf[i] == search)
2965 buf->buf[i] = replace;
2971 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2972 * @param buf Buffer to modify
2974 void StrBufToUnixLF(StrBuf *buf)
2976 char *pche, *pchS, *pchT;
2980 pche = buf->buf + buf->BufUsed;
2981 pchS = pchT = buf->buf;
2987 if (*pchS != '\n') {
2996 buf->BufUsed = pchT - buf->buf;
3000 /*******************************************************************************
3001 * Iconv Wrapper; RFC822 de/encoding *
3002 *******************************************************************************/
3005 * @ingroup StrBuf_DeEnCoder
3006 * @brief Wrapper around iconv_open()
3007 * Our version adds aliases for non-standard Microsoft charsets
3008 * such as 'MS950', aliasing them to names like 'CP950'
3010 * @param tocode Target encoding
3011 * @param fromcode Source encoding
3012 * @param pic anonimized pointer to iconv struct
3014 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3017 iconv_t ic = (iconv_t)(-1) ;
3018 ic = iconv_open(tocode, fromcode);
3019 if (ic == (iconv_t)(-1) ) {
3020 char alias_fromcode[64];
3021 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3022 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3023 alias_fromcode[0] = 'C';
3024 alias_fromcode[1] = 'P';
3025 ic = iconv_open(tocode, alias_fromcode);
3028 *(iconv_t *)pic = ic;
3034 * @ingroup StrBuf_DeEnCoder
3035 * @brief find one chunk of a RFC822 encoded string
3036 * @param Buffer where to search
3037 * @param bptr where to start searching
3038 * @returns found position, NULL if none.
3040 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3043 /* Find the next ?Q? */
3044 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3047 end = strchr(bptr + 2, '?');
3052 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3053 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3054 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3055 (*(end + 2) == '?')) {
3056 /* skip on to the end of the cluster, the next ?= */
3057 end = strstr(end + 3, "?=");
3060 /* sort of half valid encoding, try to find an end. */
3061 end = strstr(bptr, "?=");
3068 * @ingroup StrBuf_DeEnCoder
3069 * @brief convert one buffer according to the preselected iconv pointer PIC
3070 * @param ConvertBuf buffer we need to translate
3071 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3072 * @param pic Pointer to the iconv-session Object
3074 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3080 char *ibuf; /**< Buffer of characters to be converted */
3081 char *obuf; /**< Buffer for converted characters */
3082 size_t ibuflen; /**< Length of input buffer */
3083 size_t obuflen; /**< Length of output buffer */
3086 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3089 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3090 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3091 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3093 ic = *(iconv_t*)pic;
3094 ibuf = ConvertBuf->buf;
3095 ibuflen = ConvertBuf->BufUsed;
3097 obuflen = TmpBuf->BufSize;
3099 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3102 if (errno == E2BIG) {
3104 IncreaseBuf(TmpBuf, 0, 0);
3109 else if (errno == EILSEQ){
3110 /* hm, invalid utf8 sequence... what to do now? */
3111 /* An invalid multibyte sequence has been encountered in the input */
3113 else if (errno == EINVAL) {
3114 /* An incomplete multibyte sequence has been encountered in the input. */
3117 FlushStrBuf(TmpBuf);
3120 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3121 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3123 /* little card game: wheres the red lady? */
3124 SwapBuffers(ConvertBuf, TmpBuf);
3125 FlushStrBuf(TmpBuf);
3132 * @ingroup StrBuf_DeEnCoder
3133 * @brief catches one RFC822 encoded segment, and decodes it.
3134 * @param Target buffer to fill with result
3135 * @param DecodeMe buffer with stuff to process
3136 * @param SegmentStart points to our current segment in DecodeMe
3137 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3138 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3139 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3140 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3142 inline static void DecodeSegment(StrBuf *Target,
3143 const StrBuf *DecodeMe,
3144 const char *SegmentStart,
3145 const char *SegmentEnd,
3147 StrBuf *ConvertBuf2,
3148 StrBuf *FoundCharset)
3154 iconv_t ic = (iconv_t)(-1);
3158 /* Now we handle foreign character sets properly encoded
3159 * in RFC2047 format.
3161 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3162 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3163 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3164 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3165 if (FoundCharset != NULL) {
3166 FlushStrBuf(FoundCharset);
3167 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3169 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3170 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3172 *encoding = toupper(*encoding);
3173 if (*encoding == 'B') { /**< base64 */
3174 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3175 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3176 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3178 ConvertBuf->BufUsed);
3180 else if (*encoding == 'Q') { /**< quoted-printable */
3184 while (pos < ConvertBuf->BufUsed)
3186 if (ConvertBuf->buf[pos] == '_')
3187 ConvertBuf->buf[pos] = ' ';
3191 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3192 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3194 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3197 ConvertBuf->BufUsed);
3200 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3203 ctdl_iconv_open("UTF-8", charset, &ic);
3204 if (ic != (iconv_t)(-1) ) {
3206 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3207 StrBufAppendBuf(Target, ConvertBuf2, 0);
3212 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3218 * @ingroup StrBuf_DeEnCoder
3219 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3220 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3221 * @param Target where to put the decoded string to
3222 * @param DecodeMe buffer with encoded string
3223 * @param DefaultCharset if we don't find one, which should we use?
3224 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3225 * put it here for later use where no string might be known.
3227 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3230 StrBuf *ConvertBuf2;
3231 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3232 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3234 StrBuf_RFC822_2_Utf8(Target,
3240 FreeStrBuf(&ConvertBuf);
3241 FreeStrBuf(&ConvertBuf2);
3245 * @ingroup StrBuf_DeEnCoder
3246 * @brief Handle subjects with RFC2047 encoding such as:
3247 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3248 * @param Target where to put the decoded string to
3249 * @param DecodeMe buffer with encoded string
3250 * @param DefaultCharset if we don't find one, which should we use?
3251 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3252 * put it here for later use where no string might be known.
3253 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3254 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3256 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3257 const StrBuf *DecodeMe,
3258 const StrBuf* DefaultCharset,
3259 StrBuf *FoundCharset,
3261 StrBuf *ConvertBuf2)
3263 StrBuf *DecodedInvalidBuf = NULL;
3264 const StrBuf *DecodeMee = DecodeMe;
3265 const char *start, *end, *next, *nextend, *ptr = NULL;
3267 iconv_t ic = (iconv_t)(-1) ;
3272 int illegal_non_rfc2047_encoding = 0;
3275 if (DecodeMe == NULL)
3277 /* Sometimes, badly formed messages contain strings which were simply
3278 * written out directly in some foreign character set instead of
3279 * using RFC2047 encoding. This is illegal but we will attempt to
3280 * handle it anyway by converting from a user-specified default
3281 * charset to UTF-8 if we see any nonprintable characters.
3284 for (i=0; i<DecodeMe->BufUsed; ++i) {
3285 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3286 illegal_non_rfc2047_encoding = 1;
3291 if ((illegal_non_rfc2047_encoding) &&
3292 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3293 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3296 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3297 if (ic != (iconv_t)(-1) ) {
3298 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3299 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3300 DecodeMee = DecodedInvalidBuf;
3306 /* pre evaluate the first pair */
3308 start = strstr(DecodeMee->buf, "=?");
3309 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3311 end = FindNextEnd (DecodeMee, start + 2);
3313 StrBufAppendBuf(Target, DecodeMee, 0);
3314 FreeStrBuf(&DecodedInvalidBuf);
3319 if (start != DecodeMee->buf) {
3322 nFront = start - DecodeMee->buf;
3323 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3326 * Since spammers will go to all sorts of absurd lengths to get their
3327 * messages through, there are LOTS of corrupt headers out there.
3328 * So, prevent a really badly formed RFC2047 header from throwing
3329 * this function into an infinite loop.
3331 while ((start != NULL) &&
3338 DecodeSegment(Target,
3346 next = strstr(end, "=?");
3348 if ((next != NULL) &&
3350 nextend = FindNextEnd(DecodeMee, next);
3351 if (nextend == NULL)
3354 /* did we find two partitions */
3355 if ((next != NULL) &&
3359 while ((ptr < next) &&
3366 * did we find a gab just filled with blanks?
3367 * if not, copy its stuff over.
3371 StrBufAppendBufPlain(Target,
3377 /* our next-pair is our new first pair now. */
3383 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3384 if ((end != NULL) && (end < nextend)) {
3386 while ( (ptr < nextend) &&
3393 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3395 FreeStrBuf(&DecodedInvalidBuf);
3398 /*******************************************************************************
3399 * Manipulating UTF-8 Strings *
3400 *******************************************************************************/
3404 * @brief evaluate the length of an utf8 special character sequence
3405 * @param Char the character to examine
3406 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3408 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3411 unsigned char test = (1<<7);
3413 if ((*CharS & 0xC0) != 0xC0)
3417 ((test & ((unsigned char)*CharS)) != 0))
3422 if ((n > 6) || ((CharE - CharS) < n))
3429 * @brief detect whether this char starts an utf-8 encoded char
3430 * @param Char character to inspect
3431 * @returns yes or no
3433 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3435 /** 11??.???? indicates an UTF8 Sequence. */
3436 return ((Char & 0xC0) == 0xC0);
3441 * @brief measure the number of glyphs in an UTF8 string...
3442 * @param Buf string to measure
3443 * @returns the number of glyphs in Buf
3445 long StrBuf_Utf8StrLen(StrBuf *Buf)
3451 if ((Buf == NULL) || (Buf->BufUsed == 0))
3454 eptr = Buf->buf + Buf->BufUsed;
3455 while ((aptr < eptr) && (*aptr != '\0')) {
3456 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3457 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3458 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3471 * @brief cuts a string after maxlen glyphs
3472 * @param Buf string to cut to maxlen glyphs
3473 * @param maxlen how long may the string become?
3474 * @returns current length of the string
3476 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3482 eptr = Buf->buf + Buf->BufUsed;
3483 while ((aptr < eptr) && (*aptr != '\0')) {
3484 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3485 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3486 while ((*aptr++ != '\0') && (m-- > 0));
3495 Buf->BufUsed = aptr - Buf->buf;
3496 return Buf->BufUsed;
3499 return Buf->BufUsed;
3507 /*******************************************************************************
3509 *******************************************************************************/
3512 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3513 #define OS_CODE 0x03 /*< unix */
3516 * @ingroup StrBuf_DeEnCoder
3517 * @brief uses the same calling syntax as compress2(), but it
3518 * creates a stream compatible with HTTP "Content-encoding: gzip"
3519 * @param dest compressed buffer
3520 * @param destLen length of the compresed data
3521 * @param source source to encode
3522 * @param sourceLen length of source to encode
3523 * @param level compression level
3525 int ZEXPORT compress_gzip(Bytef * dest,
3527 const Bytef * source,
3531 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3533 /* write gzip header */
3534 snprintf((char *) dest, *destLen,
3535 "%c%c%c%c%c%c%c%c%c%c",
3536 gz_magic[0], gz_magic[1], Z_DEFLATED,
3537 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3540 /* normal deflate */
3543 stream.next_in = (Bytef *) source;
3544 stream.avail_in = (uInt) sourceLen;
3545 stream.next_out = dest + 10L; // after header
3546 stream.avail_out = (uInt) * destLen;
3547 if ((uLong) stream.avail_out != *destLen)
3550 stream.zalloc = (alloc_func) 0;
3551 stream.zfree = (free_func) 0;
3552 stream.opaque = (voidpf) 0;
3554 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3555 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3559 err = deflate(&stream, Z_FINISH);
3560 if (err != Z_STREAM_END) {
3561 deflateEnd(&stream);
3562 return err == Z_OK ? Z_BUF_ERROR : err;
3564 *destLen = stream.total_out + 10L;
3566 /* write CRC and Length */
3567 uLong crc = crc32(0L, source, sourceLen);
3569 for (n = 0; n < 4; ++n, ++*destLen) {
3570 dest[*destLen] = (int) (crc & 0xff);
3573 uLong len = stream.total_in;
3574 for (n = 0; n < 4; ++n, ++*destLen) {
3575 dest[*destLen] = (int) (len & 0xff);
3578 err = deflateEnd(&stream);
3585 * @ingroup StrBuf_DeEnCoder
3586 * @brief compress the buffer with gzip
3587 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3588 * @param Buf buffer whose content is to be gzipped
3590 int CompressBuffer(StrBuf *Buf)
3593 char *compressed_data = NULL;
3594 size_t compressed_len, bufsize;
3597 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3598 compressed_data = malloc(compressed_len);
3600 if (compressed_data == NULL)
3602 /* Flush some space after the used payload so valgrind shuts up... */
3603 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3604 Buf->buf[Buf->BufUsed + i++] = '\0';
3605 if (compress_gzip((Bytef *) compressed_data,
3608 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3611 Buf->buf = compressed_data;
3612 Buf->BufUsed = compressed_len;
3613 Buf->BufSize = bufsize;
3614 /* Flush some space after the used payload so valgrind shuts up... */
3616 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3617 Buf->buf[Buf->BufUsed + i++] = '\0';
3620 free(compressed_data);
3622 #endif /* HAVE_ZLIB */
3626 /*******************************************************************************
3627 * File I/O; Callbacks to libevent *
3628 *******************************************************************************/
3630 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3635 if ((FB == NULL) || (FB->Buf == NULL))
3639 * check whether the read pointer is somewhere in a range
3640 * where a cut left is inexpensive
3643 if (FB->ReadWritePointer != NULL)
3647 already_read = FB->ReadWritePointer - FB->Buf->buf;
3648 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3650 if (already_read != 0) {
3653 unread = FB->Buf->BufUsed - already_read;
3655 /* else nothing to compact... */
3657 FB->ReadWritePointer = FB->Buf->buf;
3658 bufremain = FB->Buf->BufSize;
3660 else if ((unread < 64) ||
3661 (bufremain < already_read))
3664 * if its just a tiny bit remaining, or we run out of space...
3667 FB->Buf->BufUsed = unread;
3668 if (unread < already_read)
3669 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3671 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3672 FB->ReadWritePointer = FB->Buf->buf;
3673 bufremain = FB->Buf->BufSize - unread - 1;
3675 else if (bufremain < (FB->Buf->BufSize / 10))
3677 /* get a bigger buffer */
3679 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3681 FB->ReadWritePointer = FB->Buf->buf + unread;
3683 bufremain = FB->Buf->BufSize - unread - 1;
3684 /*TODO: special increase function that won't copy the already read! */
3687 else if (bufremain < 10) {
3688 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3690 FB->ReadWritePointer = FB->Buf->buf;
3692 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3697 FB->ReadWritePointer = FB->Buf->buf;
3698 bufremain = FB->Buf->BufSize - 1;
3701 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3704 FB->Buf->BufUsed += n;
3705 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3710 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3715 if ((FB == NULL) || (FB->Buf == NULL))
3718 if (FB->ReadWritePointer != NULL)
3720 WriteRemain = FB->Buf->BufUsed -
3721 (FB->ReadWritePointer -
3725 FB->ReadWritePointer = FB->Buf->buf;
3726 WriteRemain = FB->Buf->BufUsed;
3729 n = write(fd, FB->ReadWritePointer, WriteRemain);
3731 FB->ReadWritePointer += n;
3733 if (FB->ReadWritePointer ==
3734 FB->Buf->buf + FB->Buf->BufUsed)
3736 FlushStrBuf(FB->Buf);
3737 FB->ReadWritePointer = NULL;
3740 // check whether we've got something to write
3741 // get the maximum chunk plus the pointer we can send
3742 // write whats there
3743 // if not all was sent, remember the send pointer for the next time
3744 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3750 * @ingroup StrBuf_IO
3751 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3752 * @param LineBuf your line will be copied here.
3753 * @param FB BLOB with lines of text...
3754 * @param Ptr moved arround to keep the next-line across several iterations
3755 * has to be &NULL on start; will be &NotNULL on end of buffer
3756 * @returns size of copied buffer
3758 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3760 const char *aptr, *ptr, *eptr;
3763 if ((FB == NULL) || (LineBuf == NULL))
3767 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3768 FB->ReadWritePointer = StrBufNOTNULL;
3772 FlushStrBuf(LineBuf);
3773 if (FB->ReadWritePointer == NULL)
3774 ptr = aptr = FB->Buf->buf;
3776 ptr = aptr = FB->ReadWritePointer;
3778 optr = LineBuf->buf;
3779 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3780 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3782 while ((ptr <= eptr) &&
3789 LineBuf->BufUsed = optr - LineBuf->buf;
3790 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3791 optr = LineBuf->buf + LineBuf->BufUsed;
3792 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3797 if (optr > LineBuf->buf)
3799 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3800 LineBuf->BufUsed = optr - LineBuf->buf;
3802 if ((FB->ReadWritePointer != NULL) &&
3803 (FB->ReadWritePointer != FB->Buf->buf))
3805 /* Ok, the client application read all the data
3806 it was interested in so far. Since there is more to read,
3807 we now shrink the buffer, and move the rest over.
3809 StrBufCutLeft(FB->Buf,
3810 FB->ReadWritePointer - FB->Buf->buf);
3811 FB->ReadWritePointer = FB->Buf->buf;
3813 return eMustReadMore;
3816 LineBuf->BufUsed = optr - LineBuf->buf;
3818 if ((ptr <= eptr) && (*ptr == '\r'))
3820 if ((ptr <= eptr) && (*ptr == '\n'))
3824 FB->ReadWritePointer = ptr;
3827 FlushStrBuf(FB->Buf);
3828 FB->ReadWritePointer = NULL;
3831 return eReadSuccess;
3835 * @ingroup StrBuf_CHUNKED_IO
3836 * @brief check whether the chunk-buffer has more data waiting or not.
3837 * @param FB Chunk-Buffer to inspect
3839 eReadState StrBufCheckBuffer(IOBuffer *FB)
3843 if (FB->Buf->BufUsed == 0)
3844 return eReadSuccess;
3845 if (FB->ReadWritePointer == NULL)
3846 return eBufferNotEmpty;
3847 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3848 return eBufferNotEmpty;
3849 return eReadSuccess;
3852 long IOBufferStrLength(IOBuffer *FB)
3854 if ((FB == NULL) || (FB->Buf == NULL))
3856 if (FB->ReadWritePointer == NULL)
3857 return StrLength(FB->Buf);
3859 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3862 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3864 memset(FDB, 0, sizeof(FDIOBuffer));
3866 FDB->TotalSendSize = TotalSendSize;
3868 #ifndef LINUX_SENDFILE
3869 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3871 pipe(FDB->SplicePipe);
3876 void FDIOBufferDelete(FDIOBuffer *FDB)
3878 #ifndef LINUX_SENDFILE
3879 FreeStrBuf(&FDB->ChunkBuffer);
3881 close(FDB->SplicePipe[0]);
3882 close(FDB->SplicePipe[1]);
3885 close(FDB->OtherFD);
3886 memset(FDB, 0, sizeof(FDIOBuffer));
3889 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3892 #ifdef LINUX_SENDFILE
3894 sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3897 *Err = strerror(errno);
3900 FDB->ChunkSendRemain -= sent;
3901 FDB->TotalSentAlready += sent;
3902 return FDB->ChunkSendRemain;
3908 pRead = FDB->ChunkBuffer->buf;
3909 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3911 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3913 FDB->ChunkBuffer->BufUsed += nRead;
3914 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3916 else if (nRead == 0) {}
3921 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3924 FDB->TotalSentAlready += nRead;
3925 FDB->ChunkSendRemain -= nRead;
3926 return FDB->ChunkSendRemain;
3934 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3936 ssize_t sent, pipesize;
3938 #ifdef LINUX_SENDFILE
3940 pipesize = splice(FDB->IOB->fd, NULL,
3941 FDB->SplicePipe[1], NULL,
3942 FDB->ChunkSendRemain,
3943 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3946 *Err = strerror(errno);
3950 sent = splice(FDB->SplicePipe[0], NULL,
3951 FDB->OtherFD, &FDB->TotalSentAlready,
3952 pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
3955 *Err = strerror(errno);
3958 FDB->ChunkSendRemain -= sent;
3962 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
3967 FDB->ChunkBuffer->BufUsed = sent;
3969 while (nWritten < FDB->ChunkBuffer->BufUsed) {
3970 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
3972 *Err = strerror(errno);
3978 FDB->ChunkBuffer->BufUsed = 0;
3979 FDB->TotalSentAlready += sent;
3980 FDB->ChunkSendRemain -= sent;
3981 return FDB->ChunkSendRemain;
3983 else if (sent < 0) {
3984 *Err = strerror(errno);
3992 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
3998 int nSuccessLess = 0;
4002 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4003 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4005 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4006 (FDB->ChunkSendRemain > 0))
4009 tv.tv_sec = 1; /* selectresolution; */
4013 FD_SET(FDB->OtherFD, &rfds);
4014 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4015 *Error = strerror(errno);
4019 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4024 should_write = FDB->IOB->Buf->BufUsed -
4025 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4026 if (should_write > FDB->ChunkSendRemain)
4027 should_write = FDB->ChunkSendRemain;
4029 rlen = write(FDB->OtherFD,
4030 FDB->IOB->ReadWritePointer,
4033 *Error = strerror(errno);
4037 FDB->TotalSentAlready += rlen;
4038 FDB->IOB->ReadWritePointer += rlen;
4039 FDB->ChunkSendRemain -= rlen;
4041 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4043 FlushStrBuf(FDB->IOB->Buf);
4044 FDB->IOB->ReadWritePointer = NULL;
4047 if (FDB->ChunkSendRemain == 0)
4048 return eReadSuccess;
4050 return eMustReadMore;
4053 /*******************************************************************************
4054 * File I/O; Prefer buffered read since its faster! *
4055 *******************************************************************************/
4058 * @ingroup StrBuf_IO
4059 * @brief Read a line from socket
4060 * flushes and closes the FD on error
4061 * @param buf the buffer to get the input to
4062 * @param fd pointer to the filedescriptor to read
4063 * @param append Append to an existing string or replace?
4064 * @param Error strerror() on error
4065 * @returns numbers of chars read
4067 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4069 int len, rlen, slen;
4072 *Error = strerror(EINVAL);
4079 slen = len = buf->BufUsed;
4081 rlen = read(*fd, &buf->buf[len], 1);
4083 *Error = strerror(errno);
4090 if (buf->buf[len] == '\n')
4092 if (buf->buf[len] != '\r')
4094 if (len + 2 >= buf->BufSize) {
4096 buf->buf[len+1] = '\0';
4097 IncreaseBuf(buf, 1, -1);
4101 buf->buf[len] = '\0';
4106 * @ingroup StrBuf_BufferedIO
4107 * @brief Read a line from socket
4108 * flushes and closes the FD on error
4109 * @param Line the line to read from the fd / I/O Buffer
4110 * @param buf the buffer to get the input to
4111 * @param fd pointer to the filedescriptor to read
4112 * @param timeout number of successless selects until we bail out
4113 * @param selectresolution how long to wait on each select
4114 * @param Error strerror() on error
4115 * @returns numbers of chars read
4117 int StrBufTCP_read_buffered_line(StrBuf *Line,
4121 int selectresolution,
4125 int nSuccessLess = 0;
4132 if (buf->BufUsed > 0) {
4133 pch = strchr(buf->buf, '\n');
4136 len = pch - buf->buf;
4137 if (len > 0 && (*(pch - 1) == '\r') )
4139 StrBufSub(Line, buf, 0, len - rlen);
4140 StrBufCutLeft(buf, len + 1);
4145 if (buf->BufSize - buf->BufUsed < 10)
4146 IncreaseBuf(buf, 1, -1);
4148 fdflags = fcntl(*fd, F_GETFL);
4149 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4151 while ((nSuccessLess < timeout) && (pch == NULL)) {
4153 tv.tv_sec = selectresolution;
4158 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4159 *Error = strerror(errno);
4165 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4170 &buf->buf[buf->BufUsed],
4171 buf->BufSize - buf->BufUsed - 1);
4173 *Error = strerror(errno);
4178 else if (rlen > 0) {
4180 buf->BufUsed += rlen;
4181 buf->buf[buf->BufUsed] = '\0';
4182 if (buf->BufUsed + 10 > buf->BufSize) {
4183 IncreaseBuf(buf, 1, -1);
4185 pch = strchr(buf->buf, '\n');
4192 len = pch - buf->buf;
4193 if (len > 0 && (*(pch - 1) == '\r') )
4195 StrBufSub(Line, buf, 0, len - rlen);
4196 StrBufCutLeft(buf, len + 1);
4203 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4204 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4205 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4207 * @ingroup StrBuf_BufferedIO
4208 * @brief Read a line from socket
4209 * flushes and closes the FD on error
4210 * @param Line where to append our Line read from the fd / I/O Buffer;
4211 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4212 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4213 * @param fd pointer to the filedescriptor to read
4214 * @param timeout number of successless selects until we bail out
4215 * @param selectresolution how long to wait on each select
4216 * @param Error strerror() on error
4217 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4219 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4224 int selectresolution,
4227 const char *pche = NULL;
4228 const char *pos = NULL;
4230 int len, rlen, retlen;
4231 int nSuccessLess = 0;
4233 const char *pch = NULL;
4239 if ((Line == NULL) ||
4246 *Error = ErrRBLF_PreConditionFailed;
4251 if ((IOBuf->BufUsed > 0) &&
4253 (pos < IOBuf->buf + IOBuf->BufUsed))
4257 pche = IOBuf->buf + IOBuf->BufUsed;
4261 while ((pch < pche) && (*pch != '\n'))
4263 if (Line->BufUsed + 10 > Line->BufSize)
4266 apos = pcht - Line->buf;
4268 IncreaseBuf(Line, 1, -1);
4269 pcht = Line->buf + apos;
4277 if (len > 0 && (*(pch - 1) == '\r') )
4286 if ((pch >= pche) || (*pch == '\0'))
4294 if ((pch != NULL) &&
4297 if (pch + 1 >= pche) {
4310 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4312 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4313 IncreaseBuf(IOBuf, 1, -1);
4315 fdflags = fcntl(*fd, F_GETFL);
4316 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4319 while ((nSuccessLess < timeout) &&
4329 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4330 *Error = strerror(errno);
4334 *Error = ErrRBLF_SelectFailed;
4337 if (! FD_ISSET(*fd, &rfds) != 0) {
4343 &IOBuf->buf[IOBuf->BufUsed],
4344 IOBuf->BufSize - IOBuf->BufUsed - 1);
4346 *Error = strerror(errno);
4351 else if (rlen > 0) {
4353 pLF = IOBuf->buf + IOBuf->BufUsed;
4354 IOBuf->BufUsed += rlen;
4355 IOBuf->buf[IOBuf->BufUsed] = '\0';
4357 pche = IOBuf->buf + IOBuf->BufUsed;
4359 while ((pLF < pche) && (*pLF != '\n'))
4361 if ((pLF >= pche) || (*pLF == '\0'))
4364 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4368 if (pLF != NULL) apos = pLF - IOBuf->buf;
4369 IncreaseBuf(IOBuf, 1, -1);
4370 if (pLF != NULL) pLF = IOBuf->buf + apos;
4380 if (len > 0 && (*(pLF - 1) == '\r') )
4382 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4383 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4389 return retlen + len;
4391 *Error = ErrRBLF_NotEnoughSentFromServer;
4396 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4398 * @ingroup StrBuf_IO
4399 * @brief Input binary data from socket
4400 * flushes and closes the FD on error
4401 * @param Buf the buffer to get the input to
4402 * @param fd pointer to the filedescriptor to read
4403 * @param append Append to an existing string or replace?
4404 * @param nBytes the maximal number of bytes to read
4405 * @param Error strerror() on error
4406 * @returns numbers of chars read
4408 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4419 if ((Buf == NULL) || (*fd == -1))
4421 *Error = ErrRBLF_BLOBPreConditionFailed;
4426 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4427 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4429 ptr = Buf->buf + Buf->BufUsed;
4431 fdflags = fcntl(*fd, F_GETFL);
4432 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4434 while ((nRead < nBytes) &&
4444 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4445 *Error = strerror(errno);
4449 *Error = ErrRBLF_SelectFailed;
4452 if (! FD_ISSET(*fd, &rfds) != 0) {
4458 if ((rlen = read(*fd,
4460 nBytes - nRead)) == -1) {
4463 *Error = strerror(errno);
4468 Buf->BufUsed += rlen;
4470 Buf->buf[Buf->BufUsed] = '\0';
4474 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4475 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4477 * @ingroup StrBuf_BufferedIO
4478 * @brief Input binary data from socket
4479 * flushes and closes the FD on error
4480 * @param Blob put binary thing here
4481 * @param IOBuf the buffer to get the input to
4482 * @param Pos offset inside of IOBuf
4483 * @param fd pointer to the filedescriptor to read
4484 * @param append Append to an existing string or replace?
4485 * @param nBytes the maximal number of bytes to read
4486 * @param check whether we should search for '000\n' terminators in case of timeouts
4487 * @param Error strerror() on error
4488 * @returns numbers of chars read
4490 int StrBufReadBLOBBuffered(StrBuf *Blob,
4503 int nAlreadyRead = 0;
4508 int nSuccessLess = 0;
4511 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4515 *Error = ErrRBB_BLOBFPreConditionFailed;
4521 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4522 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4527 rlen = pos - IOBuf->buf;
4528 rlen = IOBuf->BufUsed - rlen;
4531 if ((IOBuf->BufUsed > 0) &&
4533 (pos < IOBuf->buf + IOBuf->BufUsed))
4535 if (rlen < nBytes) {
4536 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4537 Blob->BufUsed += rlen;
4538 Blob->buf[Blob->BufUsed] = '\0';
4539 nAlreadyRead = nRead = rlen;
4542 if (rlen >= nBytes) {
4543 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4544 Blob->BufUsed += nBytes;
4545 Blob->buf[Blob->BufUsed] = '\0';
4546 if (rlen == nBytes) {
4558 if (IOBuf->BufSize < nBytes - nRead)
4559 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4562 fdflags = fcntl(*fd, F_GETFL);
4563 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4571 while ((nSuccessLess < MaxTries) &&
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) {
4596 IOBuf->BufSize - (ptr - IOBuf->buf));
4600 *Error = strerror(errno);
4603 else if (rlen == 0){
4604 if ((check == NNN_TERM) &&
4606 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4608 StrBufPlain(Blob, HKEY("\n000\n"));
4609 StrBufCutRight(Blob, 5);
4610 return Blob->BufUsed;
4612 else if (!IsNonBlock)
4614 else if (nSuccessLess > MaxTries) {
4616 *Error = ErrRBB_too_many_selects;
4620 else if (rlen > 0) {
4624 IOBuf->BufUsed += rlen;
4627 if (nSuccessLess >= MaxTries) {
4629 *Error = ErrRBB_too_many_selects;
4633 if (nRead > nBytes) {
4634 *Pos = IOBuf->buf + nBytes;
4636 Blob->buf[Blob->BufUsed] = '\0';
4637 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4641 return nRead + nAlreadyRead;
4645 * @ingroup StrBuf_IO
4646 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4647 * @param LineBuf your line will be copied here.
4648 * @param Buf BLOB with lines of text...
4649 * @param Ptr moved arround to keep the next-line across several iterations
4650 * has to be &NULL on start; will be &NotNULL on end of buffer
4651 * @returns size of remaining buffer
4653 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4655 const char *aptr, *ptr, *eptr;
4658 if ((Buf == NULL) ||
4659 (*Ptr == StrBufNOTNULL) ||
4662 *Ptr = StrBufNOTNULL;
4666 FlushStrBuf(LineBuf);
4668 ptr = aptr = Buf->buf;
4672 optr = LineBuf->buf;
4673 eptr = Buf->buf + Buf->BufUsed;
4674 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4676 while ((ptr <= eptr) &&
4683 LineBuf->BufUsed = optr - LineBuf->buf;
4684 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4685 optr = LineBuf->buf + LineBuf->BufUsed;
4686 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4690 if ((ptr >= eptr) && (optr > LineBuf->buf))
4692 LineBuf->BufUsed = optr - LineBuf->buf;
4694 if ((ptr <= eptr) && (*ptr == '\r'))
4696 if ((ptr <= eptr) && (*ptr == '\n'))
4703 *Ptr = StrBufNOTNULL;
4706 return Buf->BufUsed - (ptr - Buf->buf);
4711 * @ingroup StrBuf_IO
4712 * @brief removes double slashes from pathnames
4713 * @param Dir directory string to filter
4714 * @param RemoveTrailingSlash allows / disallows trailing slashes
4716 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4722 while (!IsEmptyStr(a)) {
4734 if ((RemoveTrailingSlash) &&
4740 Dir->BufUsed = b - Dir->buf;