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))
518 NewBuf->buf = (char*) malloc(Siz);
519 if (NewBuf->buf == NULL)
524 NewBuf->BufSize = Siz;
526 memcpy(NewBuf->buf, ptr, CopySize);
527 NewBuf->buf[CopySize] = '\0';
528 NewBuf->BufUsed = CopySize;
531 NewBuf->buf[0] = '\0';
534 NewBuf->ConstBuf = 0;
542 * @ingroup StrBuf_DeConstructors
543 * @brief Set an existing buffer from a c-string
544 * @param Buf buffer to load
545 * @param ptr c-string to put into
546 * @param nChars set to -1 if we should work 0-terminated
547 * @returns the new length of the string
549 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
564 CopySize = strlen(ptr);
568 while ((Siz <= CopySize) && (Siz != 0))
576 if (Siz != Buf->BufSize)
577 IncreaseBuf(Buf, 0, Siz);
578 memcpy(Buf->buf, ptr, CopySize);
579 Buf->buf[CopySize] = '\0';
580 Buf->BufUsed = CopySize;
587 * @ingroup StrBuf_DeConstructors
588 * @brief use strbuf as wrapper for a string constant for easy handling
589 * @param StringConstant a string to wrap
590 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
592 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
596 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
597 NewBuf->buf = (char*) StringConstant;
598 NewBuf->BufSize = SizeOfStrConstant;
599 NewBuf->BufUsed = SizeOfStrConstant;
600 NewBuf->ConstBuf = 1;
609 * @ingroup StrBuf_DeConstructors
610 * @brief flush the content of a Buf; keep its struct
611 * @param buf Buffer to flush
613 int FlushStrBuf(StrBuf *buf)
615 if ((buf == NULL) || (buf->buf == NULL))
625 * @ingroup StrBuf_DeConstructors
626 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
627 * @param buf Buffer to wipe
629 int FLUSHStrBuf(StrBuf *buf)
635 if (buf->BufUsed > 0) {
636 memset(buf->buf, 0, buf->BufUsed);
643 int hFreeDbglog = -1;
646 * @ingroup StrBuf_DeConstructors
647 * @brief Release a Buffer
648 * Its a double pointer, so it can NULL your pointer
649 * so fancy SIG11 appear instead of random results
650 * @param FreeMe Pointer Pointer to the buffer to free
652 void FreeStrBuf (StrBuf **FreeMe)
657 dbg_FreeStrBuf(FreeMe, 'F');
659 if (!(*FreeMe)->ConstBuf)
660 free((*FreeMe)->buf);
666 * @ingroup StrBuf_DeConstructors
667 * @brief flatten a Buffer to the Char * we return
668 * Its a double pointer, so it can NULL your pointer
669 * so fancy SIG11 appear instead of random results
670 * The Callee then owns the buffer and is responsible for freeing it.
671 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
672 * @returns the pointer of the buffer; Callee owns the memory thereafter.
674 char *SmashStrBuf (StrBuf **SmashMe)
678 if ((SmashMe == NULL) || (*SmashMe == NULL))
681 dbg_FreeStrBuf(SmashMe, 'S');
683 Ret = (*SmashMe)->buf;
690 * @ingroup StrBuf_DeConstructors
691 * @brief Release the buffer
692 * If you want put your StrBuf into a Hash, use this as Destructor.
693 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
695 void HFreeStrBuf (void *VFreeMe)
697 StrBuf *FreeMe = (StrBuf*)VFreeMe;
701 dbg_FreeStrBuf(SmashMe, 'H');
703 if (!FreeMe->ConstBuf)
709 /*******************************************************************************
710 * Simple string transformations *
711 *******************************************************************************/
715 * @brief Wrapper around atol
717 long StrTol(const StrBuf *Buf)
722 return atol(Buf->buf);
729 * @brief Wrapper around atoi
731 int StrToi(const StrBuf *Buf)
735 if (Buf->BufUsed > 0)
736 return atoi(Buf->buf);
743 * @brief Checks to see if the string is a pure number
744 * @param Buf The buffer to inspect
745 * @returns 1 if its a pure number, 0, if not.
747 int StrBufIsNumber(const StrBuf *Buf) {
749 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
752 strtoll(Buf->buf, &pEnd, 10);
753 if (pEnd == Buf->buf)
755 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
757 if (Buf->buf == pEnd)
763 * @ingroup StrBuf_Filler
764 * @brief modifies a Single char of the Buf
765 * You can point to it via char* or a zero-based integer
766 * @param Buf The buffer to manipulate
767 * @param ptr char* to zero; use NULL if unused
768 * @param nThChar zero based pointer into the string; use -1 if unused
769 * @param PeekValue The Character to place into the position
771 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
776 nThChar = ptr - Buf->buf;
777 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
779 Buf->buf[nThChar] = PeekValue;
784 * @ingroup StrBuf_Filler
785 * @brief modifies a range of chars of the Buf
786 * You can point to it via char* or a zero-based integer
787 * @param Buf The buffer to manipulate
788 * @param ptr char* to zero; use NULL if unused
789 * @param nThChar zero based pointer into the string; use -1 if unused
790 * @param nChars how many chars are to be flushed?
791 * @param PookValue The Character to place into that area
793 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
798 nThChar = ptr - Buf->buf;
799 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
801 if (nThChar + nChars > Buf->BufUsed)
802 nChars = Buf->BufUsed - nThChar;
804 memset(Buf->buf + nThChar, PookValue, nChars);
805 /* just to be shure... */
806 Buf->buf[Buf->BufUsed] = 0;
811 * @ingroup StrBuf_Filler
812 * @brief Append a StringBuffer to the buffer
813 * @param Buf Buffer to modify
814 * @param AppendBuf Buffer to copy at the end of our buffer
815 * @param Offset Should we start copying from an offset?
817 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
819 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
820 (Buf == NULL) || (Buf->buf == NULL))
823 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
826 AppendBuf->BufUsed + Buf->BufUsed);
828 memcpy(Buf->buf + Buf->BufUsed,
829 AppendBuf->buf + Offset,
830 AppendBuf->BufUsed - Offset);
831 Buf->BufUsed += AppendBuf->BufUsed - Offset;
832 Buf->buf[Buf->BufUsed] = '\0';
837 * @ingroup StrBuf_Filler
838 * @brief Append a C-String to the buffer
839 * @param Buf Buffer to modify
840 * @param AppendBuf Buffer to copy at the end of our buffer
841 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
842 * @param Offset Should we start copying from an offset?
844 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
847 long BufSizeRequired;
849 if ((AppendBuf == NULL) || (Buf == NULL))
853 aps = strlen(AppendBuf + Offset);
855 aps = AppendSize - Offset;
857 BufSizeRequired = Buf->BufUsed + aps + 1;
858 if (Buf->BufSize <= BufSizeRequired)
859 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
861 memcpy(Buf->buf + Buf->BufUsed,
865 Buf->buf[Buf->BufUsed] = '\0';
869 * @ingroup StrBuf_Filler
870 * @brief sprintf like function appending the formated string to the buffer
871 * vsnprintf version to wrap into own calls
872 * @param Buf Buffer to extend by format and Params
873 * @param format printf alike format to add
874 * @param ap va_list containing the items for format
876 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
884 if ((Buf == NULL) || (format == NULL))
887 BufSize = Buf->BufSize;
888 nWritten = Buf->BufSize + 1;
889 Offset = Buf->BufUsed;
890 newused = Offset + nWritten;
892 while (newused >= BufSize) {
894 nWritten = vsnprintf(Buf->buf + Offset,
895 Buf->BufSize - Offset,
898 newused = Offset + nWritten;
899 if (newused >= Buf->BufSize) {
900 if (IncreaseBuf(Buf, 1, newused) == -1)
901 return; /* TODO: error handling? */
902 newused = Buf->BufSize + 1;
905 Buf->BufUsed = Offset + nWritten;
906 BufSize = Buf->BufSize;
913 * @ingroup StrBuf_Filler
914 * @brief sprintf like function appending the formated string to the buffer
915 * @param Buf Buffer to extend by format and Params
916 * @param format printf alike format to add
918 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
926 if ((Buf == NULL) || (format == NULL))
929 BufSize = Buf->BufSize;
930 nWritten = Buf->BufSize + 1;
931 Offset = Buf->BufUsed;
932 newused = Offset + nWritten;
934 while (newused >= BufSize) {
935 va_start(arg_ptr, format);
936 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
937 Buf->BufSize - Buf->BufUsed,
940 newused = Buf->BufUsed + nWritten;
941 if (newused >= Buf->BufSize) {
942 if (IncreaseBuf(Buf, 1, newused) == -1)
943 return; /* TODO: error handling? */
944 newused = Buf->BufSize + 1;
947 Buf->BufUsed += nWritten;
948 BufSize = Buf->BufSize;
955 * @ingroup StrBuf_Filler
956 * @brief sprintf like function putting the formated string into the buffer
957 * @param Buf Buffer to extend by format and Parameters
958 * @param format printf alike format to add
960 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
965 if ((Buf == NULL) || (format == NULL))
968 nWritten = Buf->BufSize + 1;
969 while (nWritten >= Buf->BufSize) {
970 va_start(arg_ptr, format);
971 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
973 if (nWritten >= Buf->BufSize) {
974 if (IncreaseBuf(Buf, 0, 0) == -1)
975 return; /* TODO: error handling? */
976 nWritten = Buf->BufSize + 1;
979 Buf->BufUsed = nWritten ;
984 * @ingroup StrBuf_Filler
985 * @brief Callback for cURL to append the webserver reply to a buffer
986 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
987 * @param size pre-defined by the cURL API; see man 3 curl for mre info
988 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
989 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
991 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1000 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1001 return size * nmemb;
1007 * @brief extracts a substring from Source into dest
1008 * @param dest buffer to place substring into
1009 * @param Source string to copy substring from
1010 * @param Offset chars to skip from start
1011 * @param nChars number of chars to copy
1012 * @returns the number of chars copied; may be different from nChars due to the size of Source
1014 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1016 size_t NCharsRemain;
1017 if (Offset > Source->BufUsed)
1023 if (Offset + nChars < Source->BufUsed)
1025 if (nChars >= dest->BufSize)
1026 IncreaseBuf(dest, 0, nChars + 1);
1027 memcpy(dest->buf, Source->buf + Offset, nChars);
1028 dest->BufUsed = nChars;
1029 dest->buf[dest->BufUsed] = '\0';
1032 NCharsRemain = Source->BufUsed - Offset;
1033 if (NCharsRemain >= dest->BufSize)
1034 IncreaseBuf(dest, 0, NCharsRemain + 1);
1035 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1036 dest->BufUsed = NCharsRemain;
1037 dest->buf[dest->BufUsed] = '\0';
1038 return NCharsRemain;
1043 * @brief Cut nChars from the start of the string
1044 * @param Buf Buffer to modify
1045 * @param nChars how many chars should be skipped?
1047 void StrBufCutLeft(StrBuf *Buf, int nChars)
1049 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1050 if (nChars >= Buf->BufUsed) {
1054 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1055 Buf->BufUsed -= nChars;
1056 Buf->buf[Buf->BufUsed] = '\0';
1061 * @brief Cut the trailing n Chars from the string
1062 * @param Buf Buffer to modify
1063 * @param nChars how many chars should be trunkated?
1065 void StrBufCutRight(StrBuf *Buf, int nChars)
1067 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1070 if (nChars >= Buf->BufUsed) {
1074 Buf->BufUsed -= nChars;
1075 Buf->buf[Buf->BufUsed] = '\0';
1080 * @brief Cut the string after n Chars
1081 * @param Buf Buffer to modify
1082 * @param AfternChars after how many chars should we trunkate the string?
1083 * @param At if non-null and points inside of our string, cut it there.
1085 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1087 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1089 AfternChars = At - Buf->buf;
1092 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1094 Buf->BufUsed = AfternChars;
1095 Buf->buf[Buf->BufUsed] = '\0';
1101 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1102 * @param Buf the string to modify
1104 void StrBufTrim(StrBuf *Buf)
1107 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1109 while ((Buf->BufUsed > 0) &&
1110 isspace(Buf->buf[Buf->BufUsed - 1]))
1114 Buf->buf[Buf->BufUsed] = '\0';
1116 if (Buf->BufUsed == 0) return;
1118 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1121 if (delta > 0) StrBufCutLeft(Buf, delta);
1125 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1126 * @param Buf the string to modify
1128 void StrBufSpaceToBlank(StrBuf *Buf)
1132 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1135 pche = pch + Buf->BufUsed;
1144 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1150 if ((Buf == NULL) || (Buf->buf == NULL))
1152 pLeft = pBuff = Buf->buf;
1153 while (pBuff != NULL) {
1155 pBuff = strchr(pBuff, leftboundary);
1164 pRight = strchr(pBuff, rightboundary);
1166 StrBufCutAt(Buf, 0, pRight);
1168 StrBufCutLeft(Buf, pLeft - Buf->buf);
1173 * @ingroup StrBuf_Filler
1174 * @brief uppercase the contents of a buffer
1175 * @param Buf the buffer to translate
1177 void StrBufUpCase(StrBuf *Buf)
1181 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1184 pche = pch + Buf->BufUsed;
1185 while (pch < pche) {
1186 *pch = toupper(*pch);
1193 * @ingroup StrBuf_Filler
1194 * @brief lowercase the contents of a buffer
1195 * @param Buf the buffer to translate
1197 void StrBufLowerCase(StrBuf *Buf)
1201 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1204 pche = pch + Buf->BufUsed;
1205 while (pch < pche) {
1206 *pch = tolower(*pch);
1212 /*******************************************************************************
1213 * a tokenizer that kills, maims, and destroys *
1214 *******************************************************************************/
1217 * @ingroup StrBuf_Tokenizer
1218 * @brief Replace a token at a given place with a given length by another token with given length
1219 * @param Buf String where to work on
1220 * @param where where inside of the Buf is the search-token
1221 * @param HowLong How long is the token to be replaced
1222 * @param Repl Token to insert at 'where'
1223 * @param ReplLen Length of repl
1224 * @returns -1 if fail else length of resulting Buf
1226 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1227 const char *Repl, long ReplLen)
1230 if ((Buf == NULL) ||
1231 (where > Buf->BufUsed) ||
1232 (where + HowLong > Buf->BufUsed))
1235 if (where + ReplLen - HowLong > Buf->BufSize)
1236 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1239 memmove(Buf->buf + where + ReplLen,
1240 Buf->buf + where + HowLong,
1241 Buf->BufUsed - where - HowLong);
1243 memcpy(Buf->buf + where,
1246 Buf->BufUsed += ReplLen - HowLong;
1248 return Buf->BufUsed;
1252 * @ingroup StrBuf_Tokenizer
1253 * @brief Counts the numbmer of tokens in a buffer
1254 * @param source String to count tokens in
1255 * @param tok Tokenizer char to count
1256 * @returns numbers of tokenizer chars found
1258 int StrBufNum_tokens(const StrBuf *source, char tok)
1262 if ((source == NULL) || (source->BufUsed == 0))
1264 if ((source->BufUsed == 1) && (*source->buf == tok))
1268 pche = pch + source->BufUsed;
1279 * @ingroup StrBuf_Tokenizer
1280 * @brief a string tokenizer
1281 * @param Source StringBuffer to read into
1282 * @param parmnum n'th Parameter to remove
1283 * @param separator tokenizer character
1284 * @returns -1 if not found, else length of token.
1286 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1289 char *d, *s, *end; /* dest, source */
1292 /* Find desired @parameter */
1293 end = Source->buf + Source->BufUsed;
1295 while ((d <= end) &&
1298 /* End of string, bail! */
1303 if (*d == separator) {
1308 if ((d == NULL) || (d >= end))
1309 return 0; /* @Parameter not found */
1311 /* Find next @parameter */
1313 while ((s <= end) &&
1314 (*s && *s != separator))
1318 if (*s == separator)
1322 /* Hack and slash */
1327 memmove(d, s, Source->BufUsed - (s - Source->buf));
1328 Source->BufUsed += ReducedBy;
1329 Source->buf[Source->BufUsed] = '\0';
1331 else if (d == Source->buf) {
1333 Source->BufUsed = 0;
1337 Source->BufUsed += ReducedBy;
1350 * @ingroup StrBuf_Tokenizer
1351 * @brief a string tokenizer
1352 * @param dest Destination StringBuffer
1353 * @param Source StringBuffer to read into
1354 * @param parmnum n'th Parameter to extract
1355 * @param separator tokenizer character
1356 * @returns -1 if not found, else length of token.
1358 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1360 const char *s, *e; //* source * /
1361 int len = 0; //* running total length of extracted string * /
1362 int current_token = 0; //* token currently being processed * /
1365 dest->buf[0] = '\0';
1371 if ((Source == NULL) || (Source->BufUsed ==0)) {
1375 e = s + Source->BufUsed;
1378 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1380 while ((s < e) && !IsEmptyStr(s)) {
1381 if (*s == separator) {
1384 if (len >= dest->BufSize) {
1385 dest->BufUsed = len;
1386 if (IncreaseBuf(dest, 1, -1) < 0) {
1391 if ( (current_token == parmnum) &&
1392 (*s != separator)) {
1393 dest->buf[len] = *s;
1396 else if (current_token > parmnum) {
1402 dest->buf[len] = '\0';
1403 dest->BufUsed = len;
1405 if (current_token < parmnum) {
1406 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1409 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1418 * @ingroup StrBuf_Tokenizer
1419 * @brief a string tokenizer to fetch an integer
1420 * @param Source String containing tokens
1421 * @param parmnum n'th Parameter to extract
1422 * @param separator tokenizer character
1423 * @returns 0 if not found, else integer representation of the token
1425 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1435 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1442 * @ingroup StrBuf_Tokenizer
1443 * @brief a string tokenizer to fetch a long integer
1444 * @param Source String containing tokens
1445 * @param parmnum n'th Parameter to extract
1446 * @param separator tokenizer character
1447 * @returns 0 if not found, else long integer representation of the token
1449 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1459 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1467 * @ingroup StrBuf_Tokenizer
1468 * @brief a string tokenizer to fetch an unsigned long
1469 * @param Source String containing tokens
1470 * @param parmnum n'th Parameter to extract
1471 * @param separator tokenizer character
1472 * @returns 0 if not found, else unsigned long representation of the token
1474 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1485 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1489 return (unsigned long) atol(pnum);
1498 * @ingroup StrBuf_NextTokenizer
1499 * @brief a string tokenizer; Bounds checker
1500 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1501 * @param Source our tokenbuffer
1502 * @param pStart the token iterator pointer to inspect
1503 * @returns whether the revolving pointer is inside of the search range
1505 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1507 if ((Source == NULL) ||
1508 (*pStart == StrBufNOTNULL) ||
1509 (Source->BufUsed == 0))
1513 if (*pStart == NULL)
1517 else if (*pStart > Source->buf + Source->BufUsed)
1521 else if (*pStart <= Source->buf)
1530 * @ingroup StrBuf_NextTokenizer
1531 * @brief a string tokenizer
1532 * @param dest Destination StringBuffer
1533 * @param Source StringBuffer to read into
1534 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1535 * @param separator tokenizer
1536 * @returns -1 if not found, else length of token.
1538 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1540 const char *s; /* source */
1541 const char *EndBuffer; /* end stop of source buffer */
1542 int current_token = 0; /* token currently being processed */
1543 int len = 0; /* running total length of extracted string */
1545 if ((Source == NULL) ||
1546 (Source->BufUsed == 0) )
1548 *pStart = StrBufNOTNULL;
1554 EndBuffer = Source->buf + Source->BufUsed;
1558 dest->buf[0] = '\0';
1563 *pStart = EndBuffer + 1;
1567 if (*pStart == NULL)
1569 *pStart = Source->buf; /* we're starting to examine this buffer. */
1571 else if ((*pStart < Source->buf) ||
1572 (*pStart > EndBuffer ) )
1574 return -1; /* no more tokens to find. */
1578 /* start to find the next token */
1579 while ((s <= EndBuffer) &&
1580 (current_token == 0) )
1582 if (*s == separator)
1584 /* we found the next token */
1588 if (len >= dest->BufSize)
1590 /* our Dest-buffer isn't big enough, increase it. */
1591 dest->BufUsed = len;
1593 if (IncreaseBuf(dest, 1, -1) < 0) {
1594 /* WHUT? no more mem? bail out. */
1601 if ( (current_token == 0 ) && /* are we in our target token? */
1602 (!IsEmptyStr(s) ) &&
1603 (separator != *s) ) /* don't copy the token itself */
1605 dest->buf[len] = *s; /* Copy the payload */
1606 ++len; /* remember the bigger size. */
1612 /* did we reach the end? */
1613 if ((s > EndBuffer)) {
1614 EndBuffer = StrBufNOTNULL;
1615 *pStart = EndBuffer;
1618 *pStart = s; /* remember the position for the next run */
1621 /* sanitize our extracted token */
1622 dest->buf[len] = '\0';
1623 dest->BufUsed = len;
1630 * @ingroup StrBuf_NextTokenizer
1631 * @brief a string tokenizer
1632 * @param Source StringBuffer to read from
1633 * @param pStart pointer to the end of the last token. Feed with NULL.
1634 * @param separator tokenizer character
1635 * @param nTokens number of tokens to fastforward over
1636 * @returns -1 if not found, else length of token.
1638 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1640 const char *s, *EndBuffer; //* source * /
1641 int len = 0; //* running total length of extracted string * /
1642 int current_token = 0; //* token currently being processed * /
1644 if ((Source == NULL) ||
1645 (Source->BufUsed ==0)) {
1649 return Source->BufUsed;
1651 if (*pStart == NULL)
1652 *pStart = Source->buf;
1654 EndBuffer = Source->buf + Source->BufUsed;
1656 if ((*pStart < Source->buf) ||
1657 (*pStart > EndBuffer)) {
1665 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1667 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1668 if (*s == separator) {
1671 if (current_token >= nTokens) {
1683 * @ingroup StrBuf_NextTokenizer
1684 * @brief a string tokenizer to fetch an integer
1685 * @param Source StringBuffer to read from
1686 * @param pStart Cursor on the tokenstring
1687 * @param separator tokenizer character
1688 * @returns 0 if not found, else integer representation of the token
1690 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1700 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1707 * @ingroup StrBuf_NextTokenizer
1708 * @brief a string tokenizer to fetch a long integer
1709 * @param Source StringBuffer to read from
1710 * @param pStart Cursor on the tokenstring
1711 * @param separator tokenizer character
1712 * @returns 0 if not found, else long integer representation of the token
1714 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1724 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1732 * @ingroup StrBuf_NextTokenizer
1733 * @brief a string tokenizer to fetch an unsigned long
1734 * @param Source StringBuffer to read from
1735 * @param pStart Cursor on the tokenstring
1736 * @param separator tokenizer character
1737 * @returns 0 if not found, else unsigned long representation of the token
1739 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1750 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1754 return (unsigned long) atol(pnum);
1764 /*******************************************************************************
1765 * Escape Appending *
1766 *******************************************************************************/
1769 * @ingroup StrBuf_DeEnCoder
1770 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1771 * @param OutBuf the output buffer
1772 * @param In Buffer to encode
1773 * @param PlainIn way in from plain old c strings
1775 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1777 const char *pch, *pche;
1781 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1783 if (PlainIn != NULL) {
1784 len = strlen(PlainIn);
1790 pche = pch + In->BufUsed;
1797 pt = OutBuf->buf + OutBuf->BufUsed;
1798 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1800 while (pch < pche) {
1802 IncreaseBuf(OutBuf, 1, -1);
1803 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1804 pt = OutBuf->buf + OutBuf->BufUsed;
1807 if((*pch >= 'a' && *pch <= 'z') ||
1808 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1809 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1810 (*pch == '!') || (*pch == '_') ||
1811 (*pch == ',') || (*pch == '.'))
1818 *(pt + 1) = HexList[(unsigned char)*pch][0];
1819 *(pt + 2) = HexList[(unsigned char)*pch][1];
1821 OutBuf->BufUsed += 3;
1829 * @ingroup StrBuf_DeEnCoder
1830 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1831 * @param OutBuf the output buffer
1832 * @param In Buffer to encode
1833 * @param PlainIn way in from plain old c strings
1835 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1837 const char *pch, *pche;
1841 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1843 if (PlainIn != NULL) {
1844 len = strlen(PlainIn);
1850 pche = pch + In->BufUsed;
1857 pt = OutBuf->buf + OutBuf->BufUsed;
1858 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1860 while (pch < pche) {
1862 IncreaseBuf(OutBuf, 1, -1);
1863 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1864 pt = OutBuf->buf + OutBuf->BufUsed;
1867 if((*pch >= 'a' && *pch <= 'z') ||
1868 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1869 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1870 (*pch == '!') || (*pch == '_') ||
1871 (*pch == ',') || (*pch == '.'))
1878 *(pt + 1) = HexList[(unsigned char)*pch][0];
1879 *(pt + 2) = HexList[(unsigned char)*pch][1];
1881 OutBuf->BufUsed += 3;
1889 * @ingroup StrBuf_DeEnCoder
1890 * @brief append a string in hex encoding to the buffer
1891 * @param OutBuf the output buffer
1892 * @param In Buffer to encode
1893 * @param PlainIn way in from plain old c strings
1894 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1896 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1898 const unsigned char *pch, *pche;
1902 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1904 if (PlainIn != NULL) {
1906 len = strlen((const char*)PlainIn);
1913 pch = (const unsigned char*)In->buf;
1914 pche = pch + In->BufUsed;
1921 pt = OutBuf->buf + OutBuf->BufUsed;
1922 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1924 while (pch < pche) {
1926 IncreaseBuf(OutBuf, 1, -1);
1927 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1928 pt = OutBuf->buf + OutBuf->BufUsed;
1931 *pt = HexList[*pch][0];
1933 *pt = HexList[*pch][1];
1934 pt ++; pch ++; OutBuf->BufUsed += 2;
1940 * @ingroup StrBuf_DeEnCoder
1941 * @brief append a string in hex encoding to the buffer
1942 * @param OutBuf the output buffer
1943 * @param In Buffer to encode
1944 * @param PlainIn way in from plain old c strings
1946 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1948 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1952 * @ingroup StrBuf_DeEnCoder
1953 * @brief Append a string, escaping characters which have meaning in HTML.
1955 * @param Target target buffer
1956 * @param Source source buffer; set to NULL if you just have a C-String
1957 * @param PlainIn Plain-C string to append; set to NULL if unused
1958 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1959 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1960 * if set to 2, linebreaks are replaced by <br/>
1962 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1964 const char *aptr, *eiptr;
1968 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1971 if (PlainIn != NULL) {
1973 len = strlen(PlainIn);
1978 eiptr = aptr + Source->BufUsed;
1979 len = Source->BufUsed;
1985 bptr = Target->buf + Target->BufUsed;
1986 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1988 while (aptr < eiptr){
1990 IncreaseBuf(Target, 1, -1);
1991 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1992 bptr = Target->buf + Target->BufUsed;
1995 memcpy(bptr, "<", 4);
1997 Target->BufUsed += 4;
1999 else if (*aptr == '>') {
2000 memcpy(bptr, ">", 4);
2002 Target->BufUsed += 4;
2004 else if (*aptr == '&') {
2005 memcpy(bptr, "&", 5);
2007 Target->BufUsed += 5;
2009 else if (*aptr == '"') {
2010 memcpy(bptr, """, 6);
2012 Target->BufUsed += 6;
2014 else if (*aptr == '\'') {
2015 memcpy(bptr, "'", 5);
2017 Target->BufUsed += 5;
2019 else if (*aptr == LB) {
2024 else if (*aptr == RB) {
2029 else if (*aptr == QU) {
2034 else if ((*aptr == 32) && (nbsp == 1)) {
2035 memcpy(bptr, " ", 6);
2037 Target->BufUsed += 6;
2039 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2040 *bptr='\0'; /* nothing */
2042 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2043 memcpy(bptr, "<br/>", 11);
2045 Target->BufUsed += 11;
2049 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2050 *bptr='\0'; /* nothing */
2060 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2062 return Target->BufUsed;
2066 * @ingroup StrBuf_DeEnCoder
2067 * @brief Append a string, escaping characters which have meaning in HTML.
2068 * Converts linebreaks into blanks; escapes single quotes
2069 * @param Target target buffer
2070 * @param Source source buffer; set to NULL if you just have a C-String
2071 * @param PlainIn Plain-C string to append; set to NULL if unused
2073 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2075 const char *aptr, *eiptr;
2079 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2082 if (PlainIn != NULL) {
2084 len = strlen(PlainIn);
2089 eiptr = aptr + Source->BufUsed;
2090 len = Source->BufUsed;
2096 eptr = Target->buf + Target->BufSize - 8;
2097 tptr = Target->buf + Target->BufUsed;
2099 while (aptr < eiptr){
2101 IncreaseBuf(Target, 1, -1);
2102 eptr = Target->buf + Target->BufSize - 8;
2103 tptr = Target->buf + Target->BufUsed;
2106 if (*aptr == '\n') {
2110 else if (*aptr == '\r') {
2114 else if (*aptr == '\'') {
2120 Target->BufUsed += 5;
2133 * @ingroup StrBuf_DeEnCoder
2134 * @brief Append a string, escaping characters which have meaning in ICAL.
2136 * @param Target target buffer
2137 * @param Source source buffer; set to NULL if you just have a C-String
2138 * @param PlainIn Plain-C string to append; set to NULL if unused
2140 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2142 const char *aptr, *eiptr;
2146 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2149 if (PlainIn != NULL) {
2151 len = strlen(PlainIn);
2156 eiptr = aptr + Source->BufUsed;
2157 len = Source->BufUsed;
2163 eptr = Target->buf + Target->BufSize - 8;
2164 tptr = Target->buf + Target->BufUsed;
2166 while (aptr < eiptr){
2167 if(tptr + 3 >= eptr) {
2168 IncreaseBuf(Target, 1, -1);
2169 eptr = Target->buf + Target->BufSize - 8;
2170 tptr = Target->buf + Target->BufUsed;
2173 if (*aptr == '\n') {
2180 else if (*aptr == '\r') {
2187 else if (*aptr == ',') {
2203 * @ingroup StrBuf_DeEnCoder
2204 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2206 * @param Target target buffer
2207 * @param Source source buffer; set to NULL if you just have a C-String
2208 * @param PlainIn Plain-C string to append; set to NULL if unused
2209 * @returns size of result or -1
2211 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2213 const char *aptr, *eiptr;
2218 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2221 if (PlainIn != NULL) {
2223 len = strlen(PlainIn);
2228 eiptr = aptr + Source->BufUsed;
2229 len = Source->BufUsed;
2235 bptr = Target->buf + Target->BufUsed;
2236 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2238 while (aptr < eiptr){
2240 IncreaseBuf(Target, 1, -1);
2241 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2242 bptr = Target->buf + Target->BufUsed;
2246 memcpy(bptr, HKEY("\\n"));
2248 Target->BufUsed += 2;
2251 memcpy(bptr, HKEY("\\r"));
2253 Target->BufUsed += 2;
2260 Target->BufUsed += 2;
2263 if ((*(aptr + 1) == 'u') &&
2264 isxdigit(*(aptr + 2)) &&
2265 isxdigit(*(aptr + 3)) &&
2266 isxdigit(*(aptr + 4)) &&
2267 isxdigit(*(aptr + 5)))
2268 { /* oh, a unicode escaper. let it pass through. */
2269 memcpy(bptr, aptr, 6);
2272 Target->BufUsed += 6;
2280 Target->BufUsed += 2;
2288 Target->BufUsed += 2;
2295 Target->BufUsed += 2;
2302 Target->BufUsed += 2;
2305 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2306 while (IsUtf8Sequence > 0){
2309 if (--IsUtf8Sequence)
2317 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2319 return Target->BufUsed;
2323 * @ingroup StrBuf_DeEnCoder
2324 * @brief Append a string, escaping characters which have meaning in HTML + json.
2326 * @param Target target buffer
2327 * @param Source source buffer; set to NULL if you just have a C-String
2328 * @param PlainIn Plain-C string to append; set to NULL if unused
2329 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2330 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2331 * if set to 2, linebreaks are replaced by <br/>
2333 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2335 const char *aptr, *eiptr;
2338 int IsUtf8Sequence = 0;
2340 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2343 if (PlainIn != NULL) {
2345 len = strlen(PlainIn);
2350 eiptr = aptr + Source->BufUsed;
2351 len = Source->BufUsed;
2357 bptr = Target->buf + Target->BufUsed;
2358 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2360 while (aptr < eiptr){
2362 IncreaseBuf(Target, 1, -1);
2363 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2364 bptr = Target->buf + Target->BufUsed;
2368 memcpy(bptr, HKEY("<"));
2370 Target->BufUsed += 4;
2373 memcpy(bptr, HKEY(">"));
2375 Target->BufUsed += 4;
2378 memcpy(bptr, HKEY("&"));
2380 Target->BufUsed += 5;
2393 switch (nolinebreaks) {
2395 *bptr='\0'; /* nothing */
2398 memcpy(bptr, HKEY("<br/>"));
2400 Target->BufUsed += 11;
2403 memcpy(bptr, HKEY("\\n"));
2405 Target->BufUsed += 2;
2409 switch (nolinebreaks) {
2412 *bptr='\0'; /* nothing */
2415 memcpy(bptr, HKEY("\\r"));
2417 Target->BufUsed += 2;
2427 Target->BufUsed += 2;
2430 if ((*(aptr + 1) == 'u') &&
2431 isxdigit(*(aptr + 2)) &&
2432 isxdigit(*(aptr + 3)) &&
2433 isxdigit(*(aptr + 4)) &&
2434 isxdigit(*(aptr + 5)))
2435 { /* oh, a unicode escaper. let it pass through. */
2436 memcpy(bptr, aptr, 6);
2439 Target->BufUsed += 6;
2447 Target->BufUsed += 2;
2455 Target->BufUsed += 2;
2462 Target->BufUsed += 2;
2469 Target->BufUsed += 2;
2473 memcpy(bptr, HKEY(" "));
2475 Target->BufUsed += 6;
2479 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2480 while (IsUtf8Sequence > 0){
2483 if (--IsUtf8Sequence)
2491 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2493 return Target->BufUsed;
2497 * @ingroup StrBuf_DeEnCoder
2498 * @brief unhide special chars hidden to the HTML escaper
2499 * @param target buffer to put the unescaped string in
2500 * @param source buffer to unescape
2502 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2507 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2513 FlushStrBuf(target);
2515 len = source->BufUsed;
2516 for (a = 0; a < len; ++a) {
2517 if (target->BufUsed >= target->BufSize)
2518 IncreaseBuf(target, 1, -1);
2520 if (source->buf[a] == '=') {
2521 hex[0] = source->buf[a + 1];
2522 hex[1] = source->buf[a + 2];
2525 sscanf(hex, "%02x", &b);
2526 target->buf[target->BufUsed] = b;
2527 target->buf[++target->BufUsed] = 0;
2531 target->buf[target->BufUsed] = source->buf[a];
2532 target->buf[++target->BufUsed] = 0;
2539 * @ingroup StrBuf_DeEnCoder
2540 * @brief hide special chars from the HTML escapers and friends
2541 * @param target buffer to put the escaped string in
2542 * @param source buffer to escape
2544 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2549 FlushStrBuf(target);
2551 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2556 len = source->BufUsed;
2557 for (i=0; i<len; ++i) {
2558 if (target->BufUsed + 4 >= target->BufSize)
2559 IncreaseBuf(target, 1, -1);
2560 if ( (isalnum(source->buf[i])) ||
2561 (source->buf[i]=='-') ||
2562 (source->buf[i]=='_') ) {
2563 target->buf[target->BufUsed++] = source->buf[i];
2566 sprintf(&target->buf[target->BufUsed],
2568 (0xFF &source->buf[i]));
2569 target->BufUsed += 3;
2572 target->buf[target->BufUsed + 1] = '\0';
2576 /*******************************************************************************
2577 * Quoted Printable de/encoding *
2578 *******************************************************************************/
2581 * @ingroup StrBuf_DeEnCoder
2582 * @brief decode a buffer from base 64 encoding; destroys original
2583 * @param Buf Buffor to transform
2585 int StrBufDecodeBase64(StrBuf *Buf)
2589 if (Buf == NULL) return -1;
2591 xferbuf = (char*) malloc(Buf->BufSize);
2593 siz = CtdlDecodeBase64(xferbuf,
2603 * @ingroup StrBuf_DeEnCoder
2604 * @brief decode a buffer from base 64 encoding; destroys original
2605 * @param Buf Buffor to transform
2607 int StrBufDecodeHex(StrBuf *Buf)
2610 char *pch, *pche, *pchi;
2612 if (Buf == NULL) return -1;
2614 pch = pchi = Buf->buf;
2615 pche = pch + Buf->BufUsed;
2617 while (pchi < pche){
2618 ch = decode_hex(pchi);
2625 Buf->BufUsed = pch - Buf->buf;
2626 return Buf->BufUsed;
2630 * @ingroup StrBuf_DeEnCoder
2631 * @brief replace all chars >0x20 && < 0x7F with Mute
2632 * @param Mute char to put over invalid chars
2633 * @param Buf Buffor to transform
2635 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2639 if (Buf == NULL) return -1;
2640 pch = (unsigned char *)Buf->buf;
2641 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2642 if ((*pch < 0x20) || (*pch > 0x7F))
2646 return Buf->BufUsed;
2651 * @ingroup StrBuf_DeEnCoder
2652 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2653 * @param Buf Buffer to translate
2654 * @param StripBlanks Reduce several blanks to one?
2656 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2665 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2666 Buf->buf[Buf->BufUsed - 1] = '\0';
2671 while (a < Buf->BufUsed) {
2672 if (Buf->buf[a] == '+')
2674 else if (Buf->buf[a] == '%') {
2675 /* don't let % chars through, rather truncate the input. */
2676 if (a + 2 > Buf->BufUsed) {
2681 hex[0] = Buf->buf[a + 1];
2682 hex[1] = Buf->buf[a + 2];
2685 sscanf(hex, "%02x", &b);
2686 Buf->buf[a] = (char) b;
2687 len = Buf->BufUsed - a - 2;
2689 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2701 * @ingroup StrBuf_DeEnCoder
2702 * @brief RFC2047-encode a header field if necessary.
2703 * If no non-ASCII characters are found, the string
2704 * will be copied verbatim without encoding.
2706 * @param target Target buffer.
2707 * @param source Source string to be encoded.
2708 * @returns encoded length; -1 if non success.
2710 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2712 const char headerStr[] = "=?UTF-8?Q?";
2713 int need_to_encode = 0;
2717 if ((source == NULL) ||
2721 while ((i < source->BufUsed) &&
2722 (!IsEmptyStr (&source->buf[i])) &&
2723 (need_to_encode == 0)) {
2724 if (((unsigned char) source->buf[i] < 32) ||
2725 ((unsigned char) source->buf[i] > 126)) {
2731 if (!need_to_encode) {
2732 if (*target == NULL) {
2733 *target = NewStrBufPlain(source->buf, source->BufUsed);
2736 FlushStrBuf(*target);
2737 StrBufAppendBuf(*target, source, 0);
2740 return (*target)->BufUsed;
2744 if (*target == NULL)
2745 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2746 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2747 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2748 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2749 (*target)->BufUsed = sizeof(headerStr) - 1;
2750 for (i=0; (i < source->BufUsed); ++i) {
2751 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2752 IncreaseBuf(*target, 1, 0);
2753 ch = (unsigned char) source->buf[i];
2763 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2764 (*target)->BufUsed += 3;
2768 (*target)->buf[(*target)->BufUsed] = '_';
2770 (*target)->buf[(*target)->BufUsed] = ch;
2771 (*target)->BufUsed++;
2775 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2776 IncreaseBuf(*target, 1, 0);
2778 (*target)->buf[(*target)->BufUsed++] = '?';
2779 (*target)->buf[(*target)->BufUsed++] = '=';
2780 (*target)->buf[(*target)->BufUsed] = '\0';
2781 return (*target)->BufUsed;;
2786 static void AddRecipient(StrBuf *Target,
2788 StrBuf *EmailAddress,
2793 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2794 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2796 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2797 StrBufRFC2047encode(&EncBuf, UserName);
2798 StrBufAppendBuf(Target, EncBuf, 0);
2799 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2800 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2802 if (StrLength(EmailAddress) > 0){
2803 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2804 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2805 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2811 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2812 * \param Recp Source list of email recipients
2813 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2814 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2815 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2816 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2818 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2820 StrBuf *EmailAddress,
2824 const char *pch, *pche;
2825 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2827 if ((Recp == NULL) || (StrLength(Recp) == 0))
2831 pche = pch + StrLength(Recp);
2833 if (!CheckEncode(pch, -1, pche))
2834 return NewStrBufDup(Recp);
2836 Target = NewStrBufPlain(NULL, StrLength(Recp));
2838 while ((pch != NULL) && (pch < pche))
2840 while (isspace(*pch)) pch++;
2841 UserEnd = EmailStart = EmailEnd = NULL;
2843 if ((*pch == '"') || (*pch == '\'')) {
2844 UserStart = pch + 1;
2846 UserEnd = strchr(UserStart, *pch);
2847 if (UserEnd == NULL)
2848 break; ///TODO: Userfeedback??
2849 EmailStart = UserEnd + 1;
2850 while (isspace(*EmailStart))
2852 if (UserEnd == UserStart) {
2853 UserStart = UserEnd = NULL;
2856 if (*EmailStart == '<') {
2858 EmailEnd = strchr(EmailStart, '>');
2859 if (EmailEnd == NULL)
2860 EmailEnd = strchr(EmailStart, ',');
2864 EmailEnd = strchr(EmailStart, ',');
2866 if (EmailEnd == NULL)
2873 EmailEnd = strchr(UserStart, ',');
2874 if (EmailEnd == NULL) {
2875 EmailEnd = strchr(pch, '>');
2877 if (EmailEnd != NULL) {
2887 while ((EmailEnd > UserStart) && !gt &&
2888 ((*EmailEnd == ',') ||
2889 (*EmailEnd == '>') ||
2890 (isspace(*EmailEnd))))
2892 if (*EmailEnd == '>')
2897 if (EmailEnd == UserStart)
2901 EmailStart = strchr(UserStart, '<');
2902 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2904 UserEnd = EmailStart;
2906 while ((UserEnd > UserStart) &&
2907 isspace (*(UserEnd - 1)))
2910 if (UserStart >= UserEnd)
2911 UserStart = UserEnd = NULL;
2913 else { /* this is a local recipient... no domain, just a realname */
2914 EmailStart = UserStart;
2915 At = strchr(EmailStart, '@');
2921 EmailStart = UserStart;
2927 if ((UserStart != NULL) && (UserEnd != NULL))
2928 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2929 else if ((UserStart != NULL) && (UserEnd == NULL))
2930 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2932 FlushStrBuf(UserName);
2934 if ((EmailStart != NULL) && (EmailEnd != NULL))
2935 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2936 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2937 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2939 FlushStrBuf(EmailAddress);
2941 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2946 if ((pch != NULL) && (*pch == ','))
2948 if (pch != NULL) while (isspace(*pch))
2957 * @brief replaces all occurances of 'search' by 'replace'
2958 * @param buf Buffer to modify
2959 * @param search character to search
2960 * @param replace character to replace search by
2962 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2967 for (i=0; i<buf->BufUsed; i++)
2968 if (buf->buf[i] == search)
2969 buf->buf[i] = replace;
2975 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2976 * @param buf Buffer to modify
2978 void StrBufToUnixLF(StrBuf *buf)
2980 char *pche, *pchS, *pchT;
2984 pche = buf->buf + buf->BufUsed;
2985 pchS = pchT = buf->buf;
2991 if (*pchS != '\n') {
3000 buf->BufUsed = pchT - buf->buf;
3004 /*******************************************************************************
3005 * Iconv Wrapper; RFC822 de/encoding *
3006 *******************************************************************************/
3009 * @ingroup StrBuf_DeEnCoder
3010 * @brief Wrapper around iconv_open()
3011 * Our version adds aliases for non-standard Microsoft charsets
3012 * such as 'MS950', aliasing them to names like 'CP950'
3014 * @param tocode Target encoding
3015 * @param fromcode Source encoding
3016 * @param pic anonimized pointer to iconv struct
3018 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3021 iconv_t ic = (iconv_t)(-1) ;
3022 ic = iconv_open(tocode, fromcode);
3023 if (ic == (iconv_t)(-1) ) {
3024 char alias_fromcode[64];
3025 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3026 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3027 alias_fromcode[0] = 'C';
3028 alias_fromcode[1] = 'P';
3029 ic = iconv_open(tocode, alias_fromcode);
3032 *(iconv_t *)pic = ic;
3038 * @ingroup StrBuf_DeEnCoder
3039 * @brief find one chunk of a RFC822 encoded string
3040 * @param Buffer where to search
3041 * @param bptr where to start searching
3042 * @returns found position, NULL if none.
3044 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3047 /* Find the next ?Q? */
3048 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3051 end = strchr(bptr + 2, '?');
3056 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3057 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3058 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3059 (*(end + 2) == '?')) {
3060 /* skip on to the end of the cluster, the next ?= */
3061 end = strstr(end + 3, "?=");
3064 /* sort of half valid encoding, try to find an end. */
3065 end = strstr(bptr, "?=");
3072 * @ingroup StrBuf_DeEnCoder
3073 * @brief convert one buffer according to the preselected iconv pointer PIC
3074 * @param ConvertBuf buffer we need to translate
3075 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3076 * @param pic Pointer to the iconv-session Object
3078 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3084 char *ibuf; /**< Buffer of characters to be converted */
3085 char *obuf; /**< Buffer for converted characters */
3086 size_t ibuflen; /**< Length of input buffer */
3087 size_t obuflen; /**< Length of output buffer */
3090 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3093 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3094 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3095 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3097 ic = *(iconv_t*)pic;
3098 ibuf = ConvertBuf->buf;
3099 ibuflen = ConvertBuf->BufUsed;
3101 obuflen = TmpBuf->BufSize;
3103 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3106 if (errno == E2BIG) {
3108 IncreaseBuf(TmpBuf, 0, 0);
3113 else if (errno == EILSEQ){
3114 /* hm, invalid utf8 sequence... what to do now? */
3115 /* An invalid multibyte sequence has been encountered in the input */
3117 else if (errno == EINVAL) {
3118 /* An incomplete multibyte sequence has been encountered in the input. */
3121 FlushStrBuf(TmpBuf);
3124 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3125 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3127 /* little card game: wheres the red lady? */
3128 SwapBuffers(ConvertBuf, TmpBuf);
3129 FlushStrBuf(TmpBuf);
3136 * @ingroup StrBuf_DeEnCoder
3137 * @brief catches one RFC822 encoded segment, and decodes it.
3138 * @param Target buffer to fill with result
3139 * @param DecodeMe buffer with stuff to process
3140 * @param SegmentStart points to our current segment in DecodeMe
3141 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3142 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3143 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3144 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3146 inline static void DecodeSegment(StrBuf *Target,
3147 const StrBuf *DecodeMe,
3148 const char *SegmentStart,
3149 const char *SegmentEnd,
3151 StrBuf *ConvertBuf2,
3152 StrBuf *FoundCharset)
3158 iconv_t ic = (iconv_t)(-1);
3162 /* Now we handle foreign character sets properly encoded
3163 * in RFC2047 format.
3165 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3166 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3167 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3168 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3169 if (FoundCharset != NULL) {
3170 FlushStrBuf(FoundCharset);
3171 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3173 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3174 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3176 *encoding = toupper(*encoding);
3177 if (*encoding == 'B') { /**< base64 */
3178 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3179 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3180 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3182 ConvertBuf->BufUsed);
3184 else if (*encoding == 'Q') { /**< quoted-printable */
3188 while (pos < ConvertBuf->BufUsed)
3190 if (ConvertBuf->buf[pos] == '_')
3191 ConvertBuf->buf[pos] = ' ';
3195 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3196 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3198 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3201 ConvertBuf->BufUsed);
3204 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3207 ctdl_iconv_open("UTF-8", charset, &ic);
3208 if (ic != (iconv_t)(-1) ) {
3210 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3211 StrBufAppendBuf(Target, ConvertBuf2, 0);
3216 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3222 * @ingroup StrBuf_DeEnCoder
3223 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3224 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3225 * @param Target where to put the decoded string to
3226 * @param DecodeMe buffer with encoded string
3227 * @param DefaultCharset if we don't find one, which should we use?
3228 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3229 * put it here for later use where no string might be known.
3231 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3234 StrBuf *ConvertBuf2;
3235 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3236 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3238 StrBuf_RFC822_2_Utf8(Target,
3244 FreeStrBuf(&ConvertBuf);
3245 FreeStrBuf(&ConvertBuf2);
3249 * @ingroup StrBuf_DeEnCoder
3250 * @brief Handle subjects with RFC2047 encoding such as:
3251 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3252 * @param Target where to put the decoded string to
3253 * @param DecodeMe buffer with encoded string
3254 * @param DefaultCharset if we don't find one, which should we use?
3255 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3256 * put it here for later use where no string might be known.
3257 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3258 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3260 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3261 const StrBuf *DecodeMe,
3262 const StrBuf* DefaultCharset,
3263 StrBuf *FoundCharset,
3265 StrBuf *ConvertBuf2)
3267 StrBuf *DecodedInvalidBuf = NULL;
3268 const StrBuf *DecodeMee = DecodeMe;
3269 const char *start, *end, *next, *nextend, *ptr = NULL;
3271 iconv_t ic = (iconv_t)(-1) ;
3276 int illegal_non_rfc2047_encoding = 0;
3279 if (DecodeMe == NULL)
3281 /* Sometimes, badly formed messages contain strings which were simply
3282 * written out directly in some foreign character set instead of
3283 * using RFC2047 encoding. This is illegal but we will attempt to
3284 * handle it anyway by converting from a user-specified default
3285 * charset to UTF-8 if we see any nonprintable characters.
3288 for (i=0; i<DecodeMe->BufUsed; ++i) {
3289 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3290 illegal_non_rfc2047_encoding = 1;
3295 if ((illegal_non_rfc2047_encoding) &&
3296 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3297 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3300 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3301 if (ic != (iconv_t)(-1) ) {
3302 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3303 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3304 DecodeMee = DecodedInvalidBuf;
3310 /* pre evaluate the first pair */
3312 start = strstr(DecodeMee->buf, "=?");
3313 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3315 end = FindNextEnd (DecodeMee, start + 2);
3317 StrBufAppendBuf(Target, DecodeMee, 0);
3318 FreeStrBuf(&DecodedInvalidBuf);
3323 if (start != DecodeMee->buf) {
3326 nFront = start - DecodeMee->buf;
3327 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3330 * Since spammers will go to all sorts of absurd lengths to get their
3331 * messages through, there are LOTS of corrupt headers out there.
3332 * So, prevent a really badly formed RFC2047 header from throwing
3333 * this function into an infinite loop.
3335 while ((start != NULL) &&
3342 DecodeSegment(Target,
3350 next = strstr(end, "=?");
3352 if ((next != NULL) &&
3354 nextend = FindNextEnd(DecodeMee, next);
3355 if (nextend == NULL)
3358 /* did we find two partitions */
3359 if ((next != NULL) &&
3363 while ((ptr < next) &&
3370 * did we find a gab just filled with blanks?
3371 * if not, copy its stuff over.
3375 StrBufAppendBufPlain(Target,
3381 /* our next-pair is our new first pair now. */
3387 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3388 if ((end != NULL) && (end < nextend)) {
3390 while ( (ptr < nextend) &&
3397 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3399 FreeStrBuf(&DecodedInvalidBuf);
3402 /*******************************************************************************
3403 * Manipulating UTF-8 Strings *
3404 *******************************************************************************/
3408 * @brief evaluate the length of an utf8 special character sequence
3409 * @param Char the character to examine
3410 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3412 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3415 unsigned char test = (1<<7);
3417 if ((*CharS & 0xC0) != 0xC0)
3421 ((test & ((unsigned char)*CharS)) != 0))
3426 if ((n > 6) || ((CharE - CharS) < n))
3433 * @brief detect whether this char starts an utf-8 encoded char
3434 * @param Char character to inspect
3435 * @returns yes or no
3437 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3439 /** 11??.???? indicates an UTF8 Sequence. */
3440 return ((Char & 0xC0) == 0xC0);
3445 * @brief measure the number of glyphs in an UTF8 string...
3446 * @param Buf string to measure
3447 * @returns the number of glyphs in Buf
3449 long StrBuf_Utf8StrLen(StrBuf *Buf)
3455 if ((Buf == NULL) || (Buf->BufUsed == 0))
3458 eptr = Buf->buf + Buf->BufUsed;
3459 while ((aptr < eptr) && (*aptr != '\0')) {
3460 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3461 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3462 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3475 * @brief cuts a string after maxlen glyphs
3476 * @param Buf string to cut to maxlen glyphs
3477 * @param maxlen how long may the string become?
3478 * @returns current length of the string
3480 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3486 eptr = Buf->buf + Buf->BufUsed;
3487 while ((aptr < eptr) && (*aptr != '\0')) {
3488 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3489 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3490 while ((*aptr++ != '\0') && (m-- > 0));
3499 Buf->BufUsed = aptr - Buf->buf;
3500 return Buf->BufUsed;
3503 return Buf->BufUsed;
3511 /*******************************************************************************
3513 *******************************************************************************/
3516 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3517 #define OS_CODE 0x03 /*< unix */
3520 * @ingroup StrBuf_DeEnCoder
3521 * @brief uses the same calling syntax as compress2(), but it
3522 * creates a stream compatible with HTTP "Content-encoding: gzip"
3523 * @param dest compressed buffer
3524 * @param destLen length of the compresed data
3525 * @param source source to encode
3526 * @param sourceLen length of source to encode
3527 * @param level compression level
3529 int ZEXPORT compress_gzip(Bytef * dest,
3531 const Bytef * source,
3535 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3537 /* write gzip header */
3538 snprintf((char *) dest, *destLen,
3539 "%c%c%c%c%c%c%c%c%c%c",
3540 gz_magic[0], gz_magic[1], Z_DEFLATED,
3541 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3544 /* normal deflate */
3547 stream.next_in = (Bytef *) source;
3548 stream.avail_in = (uInt) sourceLen;
3549 stream.next_out = dest + 10L; // after header
3550 stream.avail_out = (uInt) * destLen;
3551 if ((uLong) stream.avail_out != *destLen)
3554 stream.zalloc = (alloc_func) 0;
3555 stream.zfree = (free_func) 0;
3556 stream.opaque = (voidpf) 0;
3558 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3559 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3563 err = deflate(&stream, Z_FINISH);
3564 if (err != Z_STREAM_END) {
3565 deflateEnd(&stream);
3566 return err == Z_OK ? Z_BUF_ERROR : err;
3568 *destLen = stream.total_out + 10L;
3570 /* write CRC and Length */
3571 uLong crc = crc32(0L, source, sourceLen);
3573 for (n = 0; n < 4; ++n, ++*destLen) {
3574 dest[*destLen] = (int) (crc & 0xff);
3577 uLong len = stream.total_in;
3578 for (n = 0; n < 4; ++n, ++*destLen) {
3579 dest[*destLen] = (int) (len & 0xff);
3582 err = deflateEnd(&stream);
3589 * @ingroup StrBuf_DeEnCoder
3590 * @brief compress the buffer with gzip
3591 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3592 * @param Buf buffer whose content is to be gzipped
3594 int CompressBuffer(StrBuf *Buf)
3597 char *compressed_data = NULL;
3598 size_t compressed_len, bufsize;
3601 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3602 compressed_data = malloc(compressed_len);
3604 if (compressed_data == NULL)
3606 /* Flush some space after the used payload so valgrind shuts up... */
3607 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3608 Buf->buf[Buf->BufUsed + i++] = '\0';
3609 if (compress_gzip((Bytef *) compressed_data,
3612 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3615 Buf->buf = compressed_data;
3616 Buf->BufUsed = compressed_len;
3617 Buf->BufSize = bufsize;
3618 /* Flush some space after the used payload so valgrind shuts up... */
3620 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3621 Buf->buf[Buf->BufUsed + i++] = '\0';
3624 free(compressed_data);
3626 #endif /* HAVE_ZLIB */
3630 /*******************************************************************************
3631 * File I/O; Callbacks to libevent *
3632 *******************************************************************************/
3634 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3639 if ((FB == NULL) || (FB->Buf == NULL))
3643 * check whether the read pointer is somewhere in a range
3644 * where a cut left is inexpensive
3647 if (FB->ReadWritePointer != NULL)
3651 already_read = FB->ReadWritePointer - FB->Buf->buf;
3652 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3654 if (already_read != 0) {
3657 unread = FB->Buf->BufUsed - already_read;
3659 /* else nothing to compact... */
3661 FB->ReadWritePointer = FB->Buf->buf;
3662 bufremain = FB->Buf->BufSize;
3664 else if ((unread < 64) ||
3665 (bufremain < already_read))
3668 * if its just a tiny bit remaining, or we run out of space...
3671 FB->Buf->BufUsed = unread;
3672 if (unread < already_read)
3673 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3675 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3676 FB->ReadWritePointer = FB->Buf->buf;
3677 bufremain = FB->Buf->BufSize - unread - 1;
3679 else if (bufremain < (FB->Buf->BufSize / 10))
3681 /* get a bigger buffer */
3683 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3685 FB->ReadWritePointer = FB->Buf->buf + unread;
3687 bufremain = FB->Buf->BufSize - unread - 1;
3688 /*TODO: special increase function that won't copy the already read! */
3691 else if (bufremain < 10) {
3692 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3694 FB->ReadWritePointer = FB->Buf->buf;
3696 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3701 FB->ReadWritePointer = FB->Buf->buf;
3702 bufremain = FB->Buf->BufSize - 1;
3705 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3708 FB->Buf->BufUsed += n;
3709 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3714 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3719 if ((FB == NULL) || (FB->Buf == NULL))
3722 if (FB->ReadWritePointer != NULL)
3724 WriteRemain = FB->Buf->BufUsed -
3725 (FB->ReadWritePointer -
3729 FB->ReadWritePointer = FB->Buf->buf;
3730 WriteRemain = FB->Buf->BufUsed;
3733 n = write(fd, FB->ReadWritePointer, WriteRemain);
3735 FB->ReadWritePointer += n;
3737 if (FB->ReadWritePointer ==
3738 FB->Buf->buf + FB->Buf->BufUsed)
3740 FlushStrBuf(FB->Buf);
3741 FB->ReadWritePointer = NULL;
3744 // check whether we've got something to write
3745 // get the maximum chunk plus the pointer we can send
3746 // write whats there
3747 // if not all was sent, remember the send pointer for the next time
3748 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3754 * @ingroup StrBuf_IO
3755 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3756 * @param LineBuf your line will be copied here.
3757 * @param FB BLOB with lines of text...
3758 * @param Ptr moved arround to keep the next-line across several iterations
3759 * has to be &NULL on start; will be &NotNULL on end of buffer
3760 * @returns size of copied buffer
3762 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3764 const char *aptr, *ptr, *eptr;
3767 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3771 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3772 FB->ReadWritePointer = StrBufNOTNULL;
3776 FlushStrBuf(LineBuf);
3777 if (FB->ReadWritePointer == NULL)
3778 ptr = aptr = FB->Buf->buf;
3780 ptr = aptr = FB->ReadWritePointer;
3782 optr = LineBuf->buf;
3783 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3784 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3786 while ((ptr <= eptr) &&
3793 LineBuf->BufUsed = optr - LineBuf->buf;
3794 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3795 optr = LineBuf->buf + LineBuf->BufUsed;
3796 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3801 if (optr > LineBuf->buf)
3803 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3804 LineBuf->BufUsed = optr - LineBuf->buf;
3806 if ((FB->ReadWritePointer != NULL) &&
3807 (FB->ReadWritePointer != FB->Buf->buf))
3809 /* Ok, the client application read all the data
3810 it was interested in so far. Since there is more to read,
3811 we now shrink the buffer, and move the rest over.
3813 StrBufCutLeft(FB->Buf,
3814 FB->ReadWritePointer - FB->Buf->buf);
3815 FB->ReadWritePointer = FB->Buf->buf;
3817 return eMustReadMore;
3820 LineBuf->BufUsed = optr - LineBuf->buf;
3822 if ((ptr <= eptr) && (*ptr == '\r'))
3824 if ((ptr <= eptr) && (*ptr == '\n'))
3828 FB->ReadWritePointer = ptr;
3831 FlushStrBuf(FB->Buf);
3832 FB->ReadWritePointer = NULL;
3835 return eReadSuccess;
3839 * @ingroup StrBuf_CHUNKED_IO
3840 * @brief check whether the chunk-buffer has more data waiting or not.
3841 * @param FB Chunk-Buffer to inspect
3843 eReadState StrBufCheckBuffer(IOBuffer *FB)
3847 if (FB->Buf->BufUsed == 0)
3848 return eReadSuccess;
3849 if (FB->ReadWritePointer == NULL)
3850 return eBufferNotEmpty;
3851 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3852 return eBufferNotEmpty;
3853 return eReadSuccess;
3856 long IOBufferStrLength(IOBuffer *FB)
3858 if ((FB == NULL) || (FB->Buf == NULL))
3860 if (FB->ReadWritePointer == NULL)
3861 return StrLength(FB->Buf);
3863 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3866 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3868 memset(FDB, 0, sizeof(FDIOBuffer));
3870 FDB->TotalSendSize = TotalSendSize;
3872 #ifndef LINUX_SPLICE
3873 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3875 pipe(FDB->SplicePipe);
3880 void FDIOBufferDelete(FDIOBuffer *FDB)
3882 #ifndef LINUX_SPLICE
3883 FreeStrBuf(&FDB->ChunkBuffer);
3885 close(FDB->SplicePipe[0]);
3886 close(FDB->SplicePipe[1]);
3889 close(FDB->OtherFD);
3890 memset(FDB, 0, sizeof(FDIOBuffer));
3893 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3898 sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3901 *Err = strerror(errno);
3904 FDB->ChunkSendRemain -= sent;
3905 FDB->TotalSentAlready += sent;
3906 return FDB->ChunkSendRemain;
3912 pRead = FDB->ChunkBuffer->buf;
3913 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3915 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3917 FDB->ChunkBuffer->BufUsed += nRead;
3918 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3920 else if (nRead == 0) {}
3925 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3928 FDB->TotalSentAlready += nRead;
3929 FDB->ChunkSendRemain -= nRead;
3930 return FDB->ChunkSendRemain;
3938 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3940 ssize_t sent, pipesize;
3944 pipesize = splice(FDB->IOB->fd, NULL,
3945 FDB->SplicePipe[1], NULL,
3946 FDB->ChunkSendRemain,
3947 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3950 *Err = strerror(errno);
3954 sent = splice(FDB->SplicePipe[0], NULL,
3955 FDB->OtherFD, &FDB->TotalSentAlready,
3956 pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
3959 *Err = strerror(errno);
3962 FDB->ChunkSendRemain -= sent;
3966 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
3971 FDB->ChunkBuffer->BufUsed = sent;
3973 while (nWritten < FDB->ChunkBuffer->BufUsed) {
3974 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
3976 *Err = strerror(errno);
3982 FDB->ChunkBuffer->BufUsed = 0;
3983 FDB->TotalSentAlready += sent;
3984 FDB->ChunkSendRemain -= sent;
3985 return FDB->ChunkSendRemain;
3987 else if (sent < 0) {
3988 *Err = strerror(errno);
3996 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4002 int nSuccessLess = 0;
4006 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4007 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4009 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4010 (FDB->ChunkSendRemain > 0))
4013 tv.tv_sec = 1; /* selectresolution; */
4017 FD_SET(FDB->OtherFD, &rfds);
4018 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4019 *Error = strerror(errno);
4023 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4028 should_write = FDB->IOB->Buf->BufUsed -
4029 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4030 if (should_write > FDB->ChunkSendRemain)
4031 should_write = FDB->ChunkSendRemain;
4033 rlen = write(FDB->OtherFD,
4034 FDB->IOB->ReadWritePointer,
4037 *Error = strerror(errno);
4041 FDB->TotalSentAlready += rlen;
4042 FDB->IOB->ReadWritePointer += rlen;
4043 FDB->ChunkSendRemain -= rlen;
4045 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4047 FlushStrBuf(FDB->IOB->Buf);
4048 FDB->IOB->ReadWritePointer = NULL;
4051 if (FDB->ChunkSendRemain == 0)
4052 return eReadSuccess;
4054 return eMustReadMore;
4057 /*******************************************************************************
4058 * File I/O; Prefer buffered read since its faster! *
4059 *******************************************************************************/
4062 * @ingroup StrBuf_IO
4063 * @brief Read a line from socket
4064 * flushes and closes the FD on error
4065 * @param buf the buffer to get the input to
4066 * @param fd pointer to the filedescriptor to read
4067 * @param append Append to an existing string or replace?
4068 * @param Error strerror() on error
4069 * @returns numbers of chars read
4071 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4073 int len, rlen, slen;
4075 if ((buf == NULL) || (buf->buf == NULL)) {
4076 *Error = strerror(EINVAL);
4083 slen = len = buf->BufUsed;
4085 rlen = read(*fd, &buf->buf[len], 1);
4087 *Error = strerror(errno);
4094 if (buf->buf[len] == '\n')
4096 if (buf->buf[len] != '\r')
4098 if (len + 2 >= buf->BufSize) {
4100 buf->buf[len+1] = '\0';
4101 IncreaseBuf(buf, 1, -1);
4105 buf->buf[len] = '\0';
4110 * @ingroup StrBuf_BufferedIO
4111 * @brief Read a line from socket
4112 * flushes and closes the FD on error
4113 * @param Line the line to read from the fd / I/O Buffer
4114 * @param buf the buffer to get the input to
4115 * @param fd pointer to the filedescriptor to read
4116 * @param timeout number of successless selects until we bail out
4117 * @param selectresolution how long to wait on each select
4118 * @param Error strerror() on error
4119 * @returns numbers of chars read
4121 int StrBufTCP_read_buffered_line(StrBuf *Line,
4125 int selectresolution,
4129 int nSuccessLess = 0;
4136 if (buf->BufUsed > 0) {
4137 pch = strchr(buf->buf, '\n');
4140 len = pch - buf->buf;
4141 if (len > 0 && (*(pch - 1) == '\r') )
4143 StrBufSub(Line, buf, 0, len - rlen);
4144 StrBufCutLeft(buf, len + 1);
4149 if (buf->BufSize - buf->BufUsed < 10)
4150 IncreaseBuf(buf, 1, -1);
4152 fdflags = fcntl(*fd, F_GETFL);
4153 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4155 while ((nSuccessLess < timeout) && (pch == NULL)) {
4157 tv.tv_sec = selectresolution;
4162 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4163 *Error = strerror(errno);
4169 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4174 &buf->buf[buf->BufUsed],
4175 buf->BufSize - buf->BufUsed - 1);
4177 *Error = strerror(errno);
4182 else if (rlen > 0) {
4184 buf->BufUsed += rlen;
4185 buf->buf[buf->BufUsed] = '\0';
4186 if (buf->BufUsed + 10 > buf->BufSize) {
4187 IncreaseBuf(buf, 1, -1);
4189 pch = strchr(buf->buf, '\n');
4196 len = pch - buf->buf;
4197 if (len > 0 && (*(pch - 1) == '\r') )
4199 StrBufSub(Line, buf, 0, len - rlen);
4200 StrBufCutLeft(buf, len + 1);
4207 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4208 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4209 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4211 * @ingroup StrBuf_BufferedIO
4212 * @brief Read a line from socket
4213 * flushes and closes the FD on error
4214 * @param Line where to append our Line read from the fd / I/O Buffer;
4215 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4216 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4217 * @param fd pointer to the filedescriptor to read
4218 * @param timeout number of successless selects until we bail out
4219 * @param selectresolution how long to wait on each select
4220 * @param Error strerror() on error
4221 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4223 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4228 int selectresolution,
4231 const char *pche = NULL;
4232 const char *pos = NULL;
4234 int len, rlen, retlen;
4235 int nSuccessLess = 0;
4237 const char *pch = NULL;
4243 if ((Line == NULL) ||
4250 *Error = ErrRBLF_PreConditionFailed;
4255 if ((IOBuf->BufUsed > 0) &&
4257 (pos < IOBuf->buf + IOBuf->BufUsed))
4261 pche = IOBuf->buf + IOBuf->BufUsed;
4265 while ((pch < pche) && (*pch != '\n'))
4267 if (Line->BufUsed + 10 > Line->BufSize)
4270 apos = pcht - Line->buf;
4272 IncreaseBuf(Line, 1, -1);
4273 pcht = Line->buf + apos;
4281 if (len > 0 && (*(pch - 1) == '\r') )
4290 if ((pch >= pche) || (*pch == '\0'))
4298 if ((pch != NULL) &&
4301 if (pch + 1 >= pche) {
4314 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4316 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4317 IncreaseBuf(IOBuf, 1, -1);
4319 fdflags = fcntl(*fd, F_GETFL);
4320 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4323 while ((nSuccessLess < timeout) &&
4333 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4334 *Error = strerror(errno);
4338 *Error = ErrRBLF_SelectFailed;
4341 if (! FD_ISSET(*fd, &rfds) != 0) {
4347 &IOBuf->buf[IOBuf->BufUsed],
4348 IOBuf->BufSize - IOBuf->BufUsed - 1);
4350 *Error = strerror(errno);
4355 else if (rlen > 0) {
4357 pLF = IOBuf->buf + IOBuf->BufUsed;
4358 IOBuf->BufUsed += rlen;
4359 IOBuf->buf[IOBuf->BufUsed] = '\0';
4361 pche = IOBuf->buf + IOBuf->BufUsed;
4363 while ((pLF < pche) && (*pLF != '\n'))
4365 if ((pLF >= pche) || (*pLF == '\0'))
4368 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4372 if (pLF != NULL) apos = pLF - IOBuf->buf;
4373 IncreaseBuf(IOBuf, 1, -1);
4374 if (pLF != NULL) pLF = IOBuf->buf + apos;
4384 if (len > 0 && (*(pLF - 1) == '\r') )
4386 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4387 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4393 return retlen + len;
4395 *Error = ErrRBLF_NotEnoughSentFromServer;
4400 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4402 * @ingroup StrBuf_IO
4403 * @brief Input binary data from socket
4404 * flushes and closes the FD on error
4405 * @param Buf the buffer to get the input to
4406 * @param fd pointer to the filedescriptor to read
4407 * @param append Append to an existing string or replace?
4408 * @param nBytes the maximal number of bytes to read
4409 * @param Error strerror() on error
4410 * @returns numbers of chars read
4412 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4423 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4425 *Error = ErrRBLF_BLOBPreConditionFailed;
4430 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4431 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4433 ptr = Buf->buf + Buf->BufUsed;
4435 fdflags = fcntl(*fd, F_GETFL);
4436 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4438 while ((nRead < nBytes) &&
4448 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4449 *Error = strerror(errno);
4453 *Error = ErrRBLF_SelectFailed;
4456 if (! FD_ISSET(*fd, &rfds) != 0) {
4462 if ((rlen = read(*fd,
4464 nBytes - nRead)) == -1) {
4467 *Error = strerror(errno);
4472 Buf->BufUsed += rlen;
4474 Buf->buf[Buf->BufUsed] = '\0';
4478 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4479 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4481 * @ingroup StrBuf_BufferedIO
4482 * @brief Input binary data from socket
4483 * flushes and closes the FD on error
4484 * @param Blob put binary thing here
4485 * @param IOBuf the buffer to get the input to
4486 * @param Pos offset inside of IOBuf
4487 * @param fd pointer to the filedescriptor to read
4488 * @param append Append to an existing string or replace?
4489 * @param nBytes the maximal number of bytes to read
4490 * @param check whether we should search for '000\n' terminators in case of timeouts
4491 * @param Error strerror() on error
4492 * @returns numbers of chars read
4494 int StrBufReadBLOBBuffered(StrBuf *Blob,
4507 int nAlreadyRead = 0;
4512 int nSuccessLess = 0;
4515 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4519 *Error = ErrRBB_BLOBFPreConditionFailed;
4525 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4526 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4531 rlen = pos - IOBuf->buf;
4532 rlen = IOBuf->BufUsed - rlen;
4535 if ((IOBuf->BufUsed > 0) &&
4537 (pos < IOBuf->buf + IOBuf->BufUsed))
4539 if (rlen < nBytes) {
4540 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4541 Blob->BufUsed += rlen;
4542 Blob->buf[Blob->BufUsed] = '\0';
4543 nAlreadyRead = nRead = rlen;
4546 if (rlen >= nBytes) {
4547 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4548 Blob->BufUsed += nBytes;
4549 Blob->buf[Blob->BufUsed] = '\0';
4550 if (rlen == nBytes) {
4562 if (IOBuf->BufSize < nBytes - nRead)
4563 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4566 fdflags = fcntl(*fd, F_GETFL);
4567 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4575 while ((nSuccessLess < MaxTries) &&
4585 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4586 *Error = strerror(errno);
4590 *Error = ErrRBLF_SelectFailed;
4593 if (! FD_ISSET(*fd, &rfds) != 0) {
4600 IOBuf->BufSize - (ptr - IOBuf->buf));
4604 *Error = strerror(errno);
4607 else if (rlen == 0){
4608 if ((check == NNN_TERM) &&
4610 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4612 StrBufPlain(Blob, HKEY("\n000\n"));
4613 StrBufCutRight(Blob, 5);
4614 return Blob->BufUsed;
4616 else if (!IsNonBlock)
4618 else if (nSuccessLess > MaxTries) {
4620 *Error = ErrRBB_too_many_selects;
4624 else if (rlen > 0) {
4628 IOBuf->BufUsed += rlen;
4631 if (nSuccessLess >= MaxTries) {
4633 *Error = ErrRBB_too_many_selects;
4637 if (nRead > nBytes) {
4638 *Pos = IOBuf->buf + nBytes;
4640 Blob->buf[Blob->BufUsed] = '\0';
4641 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4645 return nRead + nAlreadyRead;
4649 * @ingroup StrBuf_IO
4650 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4651 * @param LineBuf your line will be copied here.
4652 * @param Buf BLOB with lines of text...
4653 * @param Ptr moved arround to keep the next-line across several iterations
4654 * has to be &NULL on start; will be &NotNULL on end of buffer
4655 * @returns size of remaining buffer
4657 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4659 const char *aptr, *ptr, *eptr;
4662 if ((Buf == NULL) ||
4663 (*Ptr == StrBufNOTNULL) ||
4665 (LineBuf->buf == NULL))
4667 *Ptr = StrBufNOTNULL;
4671 FlushStrBuf(LineBuf);
4673 ptr = aptr = Buf->buf;
4677 optr = LineBuf->buf;
4678 eptr = Buf->buf + Buf->BufUsed;
4679 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4681 while ((ptr <= eptr) &&
4688 LineBuf->BufUsed = optr - LineBuf->buf;
4689 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4690 optr = LineBuf->buf + LineBuf->BufUsed;
4691 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4695 if ((ptr >= eptr) && (optr > LineBuf->buf))
4697 LineBuf->BufUsed = optr - LineBuf->buf;
4699 if ((ptr <= eptr) && (*ptr == '\r'))
4701 if ((ptr <= eptr) && (*ptr == '\n'))
4708 *Ptr = StrBufNOTNULL;
4711 return Buf->BufUsed - (ptr - Buf->buf);
4716 * @ingroup StrBuf_IO
4717 * @brief removes double slashes from pathnames
4718 * @param Dir directory string to filter
4719 * @param RemoveTrailingSlash allows / disallows trailing slashes
4721 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4727 while (!IsEmptyStr(a)) {
4739 if ((RemoveTrailingSlash) &&
4745 Dir->BufUsed = b - Dir->buf;