2 * Copyright (c) 1987-2013 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/select.h>
29 #include <sys/types.h>
30 #define SHOW_ME_VAPPEND_PRINTF
33 #include "libcitadel.h"
35 #include "b64/cencode.h"
36 #include "b64/cdecode.h"
52 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
53 const Bytef * source, uLong sourceLen, int level);
55 int BaseStrBufSize = 64;
58 const char *StrBufNOTNULL = ((char*) NULL) - 1;
60 const char HexList[256][3] = {
61 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
62 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
63 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
64 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
65 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
66 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
67 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
68 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
69 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
70 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
71 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
72 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
73 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
74 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
75 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
76 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
79 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
80 * StrBuf is a versatile class, aiding the handling of dynamic strings
81 * * reduce de/reallocations
82 * * reduce the need to remeasure it
83 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
84 * * allow asyncroneous IO for line and Blob based operations
85 * * reduce the use of memove in those
86 * * Quick filling in several operations with append functions
90 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
95 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
97 * use these operators to interfere with code demanding char*;
98 * if you need to own the content, smash me. Avoid, since we loose the length information.
102 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
104 * operations to get your Strings into a StrBuf, manipulating them, or appending
107 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
109 * Quick tokenizer; demands of the user to pull its tokens in sequence
113 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
115 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
119 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
121 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
122 * External Cursor to maintain the current read position inside of the buffer
123 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
127 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
133 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
135 * these functions translate the content of a buffer into another representation;
136 * some are combined Fillers and encoders
140 * Private Structure for the Stringbuffer
143 char *buf; /**< the pointer to the dynamic buffer */
144 long BufSize; /**< how many spcae do we optain */
145 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
146 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
148 long nIncreases; /**< for profiling; cound how many times we needed more */
149 char bt [SIZ]; /**< Stacktrace of last increase */
150 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
155 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
156 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
159 #ifdef HAVE_BACKTRACE
160 static void StrBufBacktrace(StrBuf *Buf, int which)
164 void *stack_frames[50];
169 pstart = pch = Buf->bt;
171 pstart = pch = Buf->bt_lastinc;
172 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
173 strings = backtrace_symbols(stack_frames, size);
174 for (i = 0; i < size; i++) {
176 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
178 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
187 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
189 if (hFreeDbglog == -1){
190 pid_t pid = getpid();
192 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
193 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
195 if ((*FreeMe)->nIncreases > 0)
199 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
201 (*FreeMe)->nIncreases,
205 (*FreeMe)->bt_lastinc);
206 n = write(hFreeDbglog, buf, n);
212 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
216 n = write(hFreeDbglog, buf, n);
220 void dbg_IncreaseBuf(StrBuf *IncMe)
223 #ifdef HAVE_BACKTRACE
224 StrBufBacktrace(Buf, 1);
228 void dbg_Init(StrBuf *Buf)
232 Buf->bt_lastinc[0] = '\0';
233 #ifdef HAVE_BACKTRACE
234 StrBufBacktrace(Buf, 0);
240 #define dbg_FreeStrBuf(a, b)
241 #define dbg_IncreaseBuf(a)
248 * @brief swaps the contents of two StrBufs
249 * this is to be used to have cheap switched between a work-buffer and a target buffer
251 * @param B second one
253 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
257 memcpy(&C, A, sizeof(*A));
258 memcpy(A, B, sizeof(*B));
259 memcpy(B, &C, sizeof(C));
264 * @ingroup StrBuf_Cast
265 * @brief Cast operator to Plain String
266 * @note if the buffer is altered by StrBuf operations, this pointer may become
267 * invalid. So don't lean on it after altering the buffer!
268 * Since this operation is considered cheap, rather call it often than risking
269 * your pointer to become invalid!
270 * @param Str the string we want to get the c-string representation for
271 * @returns the Pointer to the Content. Don't mess with it!
273 inline const char *ChrPtr(const StrBuf *Str)
281 * @ingroup StrBuf_Cast
282 * @brief since we know strlen()'s result, provide it here.
283 * @param Str the string to return the length to
284 * @returns contentlength of the buffer
286 inline int StrLength(const StrBuf *Str)
288 return (Str != NULL) ? Str->BufUsed : 0;
292 * @ingroup StrBuf_DeConstructors
293 * @brief local utility function to resize the buffer
294 * @param Buf the buffer whichs storage we should increase
295 * @param KeepOriginal should we copy the original buffer or just start over with a new one
296 * @param DestSize what should fit in after?
298 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
301 size_t NewSize = Buf->BufSize * 2;
307 while ((NewSize <= DestSize) && (NewSize != 0))
313 NewBuf= (char*) malloc(NewSize);
317 if (KeepOriginal && (Buf->BufUsed > 0))
319 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
328 Buf->BufSize = NewSize;
330 dbg_IncreaseBuf(Buf);
336 * @ingroup StrBuf_DeConstructors
337 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
338 * @param Buf Buffer to shrink (has to be empty)
339 * @param ThreshHold if the buffer is bigger then this, its readjusted
340 * @param NewSize if we Shrink it, how big are we going to be afterwards?
342 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
345 (Buf->BufUsed == 0) &&
346 (Buf->BufSize < ThreshHold)) {
348 Buf->buf = (char*) malloc(NewSize);
350 Buf->BufSize = NewSize;
355 * @ingroup StrBuf_DeConstructors
356 * @brief shrink long term buffers to their real size so they don't waste memory
357 * @param Buf buffer to shrink
358 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
359 * @returns physical size of the buffer
361 long StrBufShrinkToFit(StrBuf *Buf, int Force)
366 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
370 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
374 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
375 Buf->BufSize = Buf->BufUsed + 1;
383 * @ingroup StrBuf_DeConstructors
384 * @brief Allocate a new buffer with default buffer size
385 * @returns the new stringbuffer
387 StrBuf* NewStrBuf(void)
391 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
395 NewBuf->buf = (char*) malloc(BaseStrBufSize);
396 if (NewBuf->buf == NULL)
401 NewBuf->buf[0] = '\0';
402 NewBuf->BufSize = BaseStrBufSize;
404 NewBuf->ConstBuf = 0;
412 * @ingroup StrBuf_DeConstructors
413 * @brief Copy Constructor; returns a duplicate of CopyMe
414 * @param CopyMe Buffer to faxmilate
415 * @returns the new stringbuffer
417 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
424 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
428 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
429 if (NewBuf->buf == NULL)
435 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
436 NewBuf->BufUsed = CopyMe->BufUsed;
437 NewBuf->BufSize = CopyMe->BufSize;
438 NewBuf->ConstBuf = 0;
446 * @ingroup StrBuf_DeConstructors
447 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
448 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
449 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
450 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
451 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
452 * @returns the new stringbuffer
454 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
458 if (CreateRelpaceMe == NULL)
463 if (*CreateRelpaceMe != NULL)
464 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
466 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
470 if (CopyFlushMe == NULL)
472 if (*CreateRelpaceMe != NULL)
473 FlushStrBuf(*CreateRelpaceMe);
475 *CreateRelpaceMe = NewStrBuf();
480 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
481 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
482 * be a big IO-Buffer...
484 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
486 if (*CreateRelpaceMe == NULL)
488 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
493 NewBuf = *CreateRelpaceMe;
496 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
500 if (*CreateRelpaceMe == NULL)
502 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
506 NewBuf = *CreateRelpaceMe;
507 SwapBuffers (NewBuf, CopyFlushMe);
510 FlushStrBuf(CopyFlushMe);
515 * @ingroup StrBuf_DeConstructors
516 * @brief create a new Buffer using an existing c-string
517 * this function should also be used if you want to pre-suggest
518 * the buffer size to allocate in conjunction with ptr == NULL
519 * @param ptr the c-string to copy; may be NULL to create a blank instance
520 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
521 * @returns the new stringbuffer
523 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
526 size_t Siz = BaseStrBufSize;
529 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
534 CopySize = strlen((ptr != NULL)?ptr:"");
538 while ((Siz <= CopySize) && (Siz != 0))
547 NewBuf->buf = (char*) malloc(Siz);
548 if (NewBuf->buf == NULL)
553 NewBuf->BufSize = Siz;
555 memcpy(NewBuf->buf, ptr, CopySize);
556 NewBuf->buf[CopySize] = '\0';
557 NewBuf->BufUsed = CopySize;
560 NewBuf->buf[0] = '\0';
563 NewBuf->ConstBuf = 0;
571 * @ingroup StrBuf_DeConstructors
572 * @brief Set an existing buffer from a c-string
573 * @param Buf buffer to load
574 * @param ptr c-string to put into
575 * @param nChars set to -1 if we should work 0-terminated
576 * @returns the new length of the string
578 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
593 CopySize = strlen(ptr);
597 while ((Siz <= CopySize) && (Siz != 0))
605 if (Siz != Buf->BufSize)
606 IncreaseBuf(Buf, 0, Siz);
607 memcpy(Buf->buf, ptr, CopySize);
608 Buf->buf[CopySize] = '\0';
609 Buf->BufUsed = CopySize;
616 * @ingroup StrBuf_DeConstructors
617 * @brief use strbuf as wrapper for a string constant for easy handling
618 * @param StringConstant a string to wrap
619 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
621 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
625 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
628 NewBuf->buf = (char*) StringConstant;
629 NewBuf->BufSize = SizeOfStrConstant;
630 NewBuf->BufUsed = SizeOfStrConstant;
631 NewBuf->ConstBuf = 1;
640 * @ingroup StrBuf_DeConstructors
641 * @brief flush the content of a Buf; keep its struct
642 * @param buf Buffer to flush
644 int FlushStrBuf(StrBuf *buf)
646 if ((buf == NULL) || (buf->buf == NULL))
656 * @ingroup StrBuf_DeConstructors
657 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
658 * @param buf Buffer to wipe
660 int FLUSHStrBuf(StrBuf *buf)
666 if (buf->BufUsed > 0) {
667 memset(buf->buf, 0, buf->BufUsed);
674 int hFreeDbglog = -1;
677 * @ingroup StrBuf_DeConstructors
678 * @brief Release a Buffer
679 * Its a double pointer, so it can NULL your pointer
680 * so fancy SIG11 appear instead of random results
681 * @param FreeMe Pointer Pointer to the buffer to free
683 void FreeStrBuf (StrBuf **FreeMe)
688 dbg_FreeStrBuf(FreeMe, 'F');
690 if (!(*FreeMe)->ConstBuf)
691 free((*FreeMe)->buf);
697 * @ingroup StrBuf_DeConstructors
698 * @brief flatten a Buffer to the Char * we return
699 * Its a double pointer, so it can NULL your pointer
700 * so fancy SIG11 appear instead of random results
701 * The Callee then owns the buffer and is responsible for freeing it.
702 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
703 * @returns the pointer of the buffer; Callee owns the memory thereafter.
705 char *SmashStrBuf (StrBuf **SmashMe)
709 if ((SmashMe == NULL) || (*SmashMe == NULL))
712 dbg_FreeStrBuf(SmashMe, 'S');
714 Ret = (*SmashMe)->buf;
721 * @ingroup StrBuf_DeConstructors
722 * @brief Release the buffer
723 * If you want put your StrBuf into a Hash, use this as Destructor.
724 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
726 void HFreeStrBuf (void *VFreeMe)
728 StrBuf *FreeMe = (StrBuf*)VFreeMe;
732 dbg_FreeStrBuf(SmashMe, 'H');
734 if (!FreeMe->ConstBuf)
740 /*******************************************************************************
741 * Simple string transformations *
742 *******************************************************************************/
746 * @brief Wrapper around atol
748 long StrTol(const StrBuf *Buf)
753 return atol(Buf->buf);
760 * @brief Wrapper around atoi
762 int StrToi(const StrBuf *Buf)
766 if (Buf->BufUsed > 0)
767 return atoi(Buf->buf);
774 * @brief Checks to see if the string is a pure number
775 * @param Buf The buffer to inspect
776 * @returns 1 if its a pure number, 0, if not.
778 int StrBufIsNumber(const StrBuf *Buf) {
780 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
783 strtoll(Buf->buf, &pEnd, 10);
784 if (pEnd == Buf->buf)
786 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
788 if (Buf->buf == pEnd)
794 * @ingroup StrBuf_Filler
795 * @brief modifies a Single char of the Buf
796 * You can point to it via char* or a zero-based integer
797 * @param Buf The buffer to manipulate
798 * @param ptr char* to zero; use NULL if unused
799 * @param nThChar zero based pointer into the string; use -1 if unused
800 * @param PeekValue The Character to place into the position
802 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
807 nThChar = ptr - Buf->buf;
808 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
810 Buf->buf[nThChar] = PeekValue;
815 * @ingroup StrBuf_Filler
816 * @brief modifies a range of chars of the Buf
817 * You can point to it via char* or a zero-based integer
818 * @param Buf The buffer to manipulate
819 * @param ptr char* to zero; use NULL if unused
820 * @param nThChar zero based pointer into the string; use -1 if unused
821 * @param nChars how many chars are to be flushed?
822 * @param PookValue The Character to place into that area
824 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
829 nThChar = ptr - Buf->buf;
830 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
832 if (nThChar + nChars > Buf->BufUsed)
833 nChars = Buf->BufUsed - nThChar;
835 memset(Buf->buf + nThChar, PookValue, nChars);
836 /* just to be shure... */
837 Buf->buf[Buf->BufUsed] = 0;
842 * @ingroup StrBuf_Filler
843 * @brief Append a StringBuffer to the buffer
844 * @param Buf Buffer to modify
845 * @param AppendBuf Buffer to copy at the end of our buffer
846 * @param Offset Should we start copying from an offset?
848 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
850 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
851 (Buf == NULL) || (Buf->buf == NULL))
854 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
857 AppendBuf->BufUsed + Buf->BufUsed);
859 memcpy(Buf->buf + Buf->BufUsed,
860 AppendBuf->buf + Offset,
861 AppendBuf->BufUsed - Offset);
862 Buf->BufUsed += AppendBuf->BufUsed - Offset;
863 Buf->buf[Buf->BufUsed] = '\0';
868 * @ingroup StrBuf_Filler
869 * @brief Append a C-String to the buffer
870 * @param Buf Buffer to modify
871 * @param AppendBuf Buffer to copy at the end of our buffer
872 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
873 * @param Offset Should we start copying from an offset?
875 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
878 long BufSizeRequired;
880 if ((AppendBuf == NULL) || (Buf == NULL))
884 aps = strlen(AppendBuf + Offset);
886 aps = AppendSize - Offset;
888 BufSizeRequired = Buf->BufUsed + aps + 1;
889 if (Buf->BufSize <= BufSizeRequired)
890 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
892 memcpy(Buf->buf + Buf->BufUsed,
896 Buf->buf[Buf->BufUsed] = '\0';
900 * @ingroup StrBuf_Filler
901 * @brief sprintf like function appending the formated string to the buffer
902 * vsnprintf version to wrap into own calls
903 * @param Buf Buffer to extend by format and Params
904 * @param format printf alike format to add
905 * @param ap va_list containing the items for format
907 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
915 if ((Buf == NULL) || (format == NULL))
918 BufSize = Buf->BufSize;
919 nWritten = Buf->BufSize + 1;
920 Offset = Buf->BufUsed;
921 newused = Offset + nWritten;
923 while (newused >= BufSize) {
925 nWritten = vsnprintf(Buf->buf + Offset,
926 Buf->BufSize - Offset,
929 newused = Offset + nWritten;
930 if (newused >= Buf->BufSize) {
931 if (IncreaseBuf(Buf, 1, newused) == -1)
932 return; /* TODO: error handling? */
933 newused = Buf->BufSize + 1;
936 Buf->BufUsed = Offset + nWritten;
937 BufSize = Buf->BufSize;
944 * @ingroup StrBuf_Filler
945 * @brief sprintf like function appending the formated string to the buffer
946 * @param Buf Buffer to extend by format and Params
947 * @param format printf alike format to add
949 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
957 if ((Buf == NULL) || (format == NULL))
960 BufSize = Buf->BufSize;
961 nWritten = Buf->BufSize + 1;
962 Offset = Buf->BufUsed;
963 newused = Offset + nWritten;
965 while (newused >= BufSize) {
966 va_start(arg_ptr, format);
967 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
968 Buf->BufSize - Buf->BufUsed,
971 newused = Buf->BufUsed + nWritten;
972 if (newused >= Buf->BufSize) {
973 if (IncreaseBuf(Buf, 1, newused) == -1)
974 return; /* TODO: error handling? */
975 newused = Buf->BufSize + 1;
978 Buf->BufUsed += nWritten;
979 BufSize = Buf->BufSize;
986 * @ingroup StrBuf_Filler
987 * @brief sprintf like function putting the formated string into the buffer
988 * @param Buf Buffer to extend by format and Parameters
989 * @param format printf alike format to add
991 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
996 if ((Buf == NULL) || (format == NULL))
999 nWritten = Buf->BufSize + 1;
1000 while (nWritten >= Buf->BufSize) {
1001 va_start(arg_ptr, format);
1002 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1004 if (nWritten >= Buf->BufSize) {
1005 if (IncreaseBuf(Buf, 0, 0) == -1)
1006 return; /* TODO: error handling? */
1007 nWritten = Buf->BufSize + 1;
1010 Buf->BufUsed = nWritten ;
1015 * @ingroup StrBuf_Filler
1016 * @brief Callback for cURL to append the webserver reply to a buffer
1017 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1018 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1019 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1020 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1022 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1031 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1032 return size * nmemb;
1038 * @brief extracts a substring from Source into dest
1039 * @param dest buffer to place substring into
1040 * @param Source string to copy substring from
1041 * @param Offset chars to skip from start
1042 * @param nChars number of chars to copy
1043 * @returns the number of chars copied; may be different from nChars due to the size of Source
1045 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1047 size_t NCharsRemain;
1048 if (Offset > Source->BufUsed)
1054 if (Offset + nChars < Source->BufUsed)
1056 if ((nChars >= dest->BufSize) &&
1057 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1059 memcpy(dest->buf, Source->buf + Offset, nChars);
1060 dest->BufUsed = nChars;
1061 dest->buf[dest->BufUsed] = '\0';
1064 NCharsRemain = Source->BufUsed - Offset;
1065 if ((NCharsRemain >= dest->BufSize) &&
1066 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1068 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1069 dest->BufUsed = NCharsRemain;
1070 dest->buf[dest->BufUsed] = '\0';
1071 return NCharsRemain;
1076 * @brief Cut nChars from the start of the string
1077 * @param Buf Buffer to modify
1078 * @param nChars how many chars should be skipped?
1080 void StrBufCutLeft(StrBuf *Buf, int nChars)
1082 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1083 if (nChars >= Buf->BufUsed) {
1087 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1088 Buf->BufUsed -= nChars;
1089 Buf->buf[Buf->BufUsed] = '\0';
1094 * @brief Cut the trailing n Chars from the string
1095 * @param Buf Buffer to modify
1096 * @param nChars how many chars should be trunkated?
1098 void StrBufCutRight(StrBuf *Buf, int nChars)
1100 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1103 if (nChars >= Buf->BufUsed) {
1107 Buf->BufUsed -= nChars;
1108 Buf->buf[Buf->BufUsed] = '\0';
1113 * @brief Cut the string after n Chars
1114 * @param Buf Buffer to modify
1115 * @param AfternChars after how many chars should we trunkate the string?
1116 * @param At if non-null and points inside of our string, cut it there.
1118 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1120 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1122 AfternChars = At - Buf->buf;
1125 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1127 Buf->BufUsed = AfternChars;
1128 Buf->buf[Buf->BufUsed] = '\0';
1134 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1135 * @param Buf the string to modify
1137 void StrBufTrim(StrBuf *Buf)
1140 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1142 while ((Buf->BufUsed > 0) &&
1143 isspace(Buf->buf[Buf->BufUsed - 1]))
1147 Buf->buf[Buf->BufUsed] = '\0';
1149 if (Buf->BufUsed == 0) return;
1151 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1154 if (delta > 0) StrBufCutLeft(Buf, delta);
1158 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1159 * @param Buf the string to modify
1161 void StrBufSpaceToBlank(StrBuf *Buf)
1165 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1168 pche = pch + Buf->BufUsed;
1177 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1182 if ((Buf == NULL) || (Buf->buf == NULL)) {
1186 pRight = strchr(Buf->buf, rightboundary);
1187 if (pRight != NULL) {
1188 StrBufCutAt(Buf, 0, pRight);
1191 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1192 if (pLeft != NULL) {
1193 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1199 * @ingroup StrBuf_Filler
1200 * @brief uppercase the contents of a buffer
1201 * @param Buf the buffer to translate
1203 void StrBufUpCase(StrBuf *Buf)
1207 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1210 pche = pch + Buf->BufUsed;
1211 while (pch < pche) {
1212 *pch = toupper(*pch);
1219 * @ingroup StrBuf_Filler
1220 * @brief lowercase the contents of a buffer
1221 * @param Buf the buffer to translate
1223 void StrBufLowerCase(StrBuf *Buf)
1227 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1230 pche = pch + Buf->BufUsed;
1231 while (pch < pche) {
1232 *pch = tolower(*pch);
1238 /*******************************************************************************
1239 * a tokenizer that kills, maims, and destroys *
1240 *******************************************************************************/
1243 * @ingroup StrBuf_Tokenizer
1244 * @brief Replace a token at a given place with a given length by another token with given length
1245 * @param Buf String where to work on
1246 * @param where where inside of the Buf is the search-token
1247 * @param HowLong How long is the token to be replaced
1248 * @param Repl Token to insert at 'where'
1249 * @param ReplLen Length of repl
1250 * @returns -1 if fail else length of resulting Buf
1252 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1253 const char *Repl, long ReplLen)
1256 if ((Buf == NULL) ||
1257 (where > Buf->BufUsed) ||
1258 (where + HowLong > Buf->BufUsed))
1261 if (where + ReplLen - HowLong > Buf->BufSize)
1262 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1265 memmove(Buf->buf + where + ReplLen,
1266 Buf->buf + where + HowLong,
1267 Buf->BufUsed - where - HowLong);
1269 memcpy(Buf->buf + where,
1272 Buf->BufUsed += ReplLen - HowLong;
1274 return Buf->BufUsed;
1278 * @ingroup StrBuf_Tokenizer
1279 * @brief Counts the numbmer of tokens in a buffer
1280 * @param source String to count tokens in
1281 * @param tok Tokenizer char to count
1282 * @returns numbers of tokenizer chars found
1284 int StrBufNum_tokens(const StrBuf *source, char tok)
1288 if ((source == NULL) || (source->BufUsed == 0))
1290 if ((source->BufUsed == 1) && (*source->buf == tok))
1294 pche = pch + source->BufUsed;
1305 * @ingroup StrBuf_Tokenizer
1306 * @brief a string tokenizer
1307 * @param Source StringBuffer to read into
1308 * @param parmnum n'th Parameter to remove
1309 * @param separator tokenizer character
1310 * @returns -1 if not found, else length of token.
1312 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1315 char *d, *s, *end; /* dest, source */
1318 /* Find desired @parameter */
1319 end = Source->buf + Source->BufUsed;
1321 while ((d <= end) &&
1324 /* End of string, bail! */
1329 if (*d == separator) {
1334 if ((d == NULL) || (d >= end))
1335 return 0; /* @Parameter not found */
1337 /* Find next @parameter */
1339 while ((s <= end) &&
1340 (*s && *s != separator))
1344 if (*s == separator)
1348 /* Hack and slash */
1353 memmove(d, s, Source->BufUsed - (s - Source->buf));
1354 Source->BufUsed += ReducedBy;
1355 Source->buf[Source->BufUsed] = '\0';
1357 else if (d == Source->buf) {
1359 Source->BufUsed = 0;
1363 Source->BufUsed += ReducedBy;
1374 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1376 const StrBuf Temp = {
1389 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1393 * @ingroup StrBuf_Tokenizer
1394 * @brief a string tokenizer
1395 * @param dest Destination StringBuffer
1396 * @param Source StringBuffer to read into
1397 * @param parmnum n'th Parameter to extract
1398 * @param separator tokenizer character
1399 * @returns -1 if not found, else length of token.
1401 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1403 const char *s, *e; //* source * /
1404 int len = 0; //* running total length of extracted string * /
1405 int current_token = 0; //* token currently being processed * /
1408 dest->buf[0] = '\0';
1414 if ((Source == NULL) || (Source->BufUsed ==0)) {
1418 e = s + Source->BufUsed;
1421 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1423 while ((s < e) && !IsEmptyStr(s)) {
1424 if (*s == separator) {
1427 if (len >= dest->BufSize) {
1428 dest->BufUsed = len;
1429 if (IncreaseBuf(dest, 1, -1) < 0) {
1434 if ( (current_token == parmnum) &&
1435 (*s != separator)) {
1436 dest->buf[len] = *s;
1439 else if (current_token > parmnum) {
1445 dest->buf[len] = '\0';
1446 dest->BufUsed = len;
1448 if (current_token < parmnum) {
1449 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1452 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1461 * @ingroup StrBuf_Tokenizer
1462 * @brief a string tokenizer to fetch an integer
1463 * @param Source String containing tokens
1464 * @param parmnum n'th Parameter to extract
1465 * @param separator tokenizer character
1466 * @returns 0 if not found, else integer representation of the token
1468 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1478 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1485 * @ingroup StrBuf_Tokenizer
1486 * @brief a string tokenizer to fetch a long integer
1487 * @param Source String containing tokens
1488 * @param parmnum n'th Parameter to extract
1489 * @param separator tokenizer character
1490 * @returns 0 if not found, else long integer representation of the token
1492 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1502 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1510 * @ingroup StrBuf_Tokenizer
1511 * @brief a string tokenizer to fetch an unsigned long
1512 * @param Source String containing tokens
1513 * @param parmnum n'th Parameter to extract
1514 * @param separator tokenizer character
1515 * @returns 0 if not found, else unsigned long representation of the token
1517 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1528 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1532 return (unsigned long) atol(pnum);
1541 * @ingroup StrBuf_NextTokenizer
1542 * @brief a string tokenizer; Bounds checker
1543 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1544 * @param Source our tokenbuffer
1545 * @param pStart the token iterator pointer to inspect
1546 * @returns whether the revolving pointer is inside of the search range
1548 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1550 if ((Source == NULL) ||
1551 (*pStart == StrBufNOTNULL) ||
1552 (Source->BufUsed == 0))
1556 if (*pStart == NULL)
1560 else if (*pStart > Source->buf + Source->BufUsed)
1564 else if (*pStart <= Source->buf)
1573 * @ingroup StrBuf_NextTokenizer
1574 * @brief a string tokenizer
1575 * @param dest Destination StringBuffer
1576 * @param Source StringBuffer to read into
1577 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1578 * @param separator tokenizer
1579 * @returns -1 if not found, else length of token.
1581 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1583 const char *s; /* source */
1584 const char *EndBuffer; /* end stop of source buffer */
1585 int current_token = 0; /* token currently being processed */
1586 int len = 0; /* running total length of extracted string */
1588 if ((Source == NULL) ||
1589 (Source->BufUsed == 0) )
1591 *pStart = StrBufNOTNULL;
1597 EndBuffer = Source->buf + Source->BufUsed;
1601 dest->buf[0] = '\0';
1606 *pStart = EndBuffer + 1;
1610 if (*pStart == NULL)
1612 *pStart = Source->buf; /* we're starting to examine this buffer. */
1614 else if ((*pStart < Source->buf) ||
1615 (*pStart > EndBuffer ) )
1617 return -1; /* no more tokens to find. */
1621 /* start to find the next token */
1622 while ((s <= EndBuffer) &&
1623 (current_token == 0) )
1625 if (*s == separator)
1627 /* we found the next token */
1631 if (len >= dest->BufSize)
1633 /* our Dest-buffer isn't big enough, increase it. */
1634 dest->BufUsed = len;
1636 if (IncreaseBuf(dest, 1, -1) < 0) {
1637 /* WHUT? no more mem? bail out. */
1644 if ( (current_token == 0 ) && /* are we in our target token? */
1645 (!IsEmptyStr(s) ) &&
1646 (separator != *s) ) /* don't copy the token itself */
1648 dest->buf[len] = *s; /* Copy the payload */
1649 ++len; /* remember the bigger size. */
1655 /* did we reach the end? */
1656 if ((s > EndBuffer)) {
1657 EndBuffer = StrBufNOTNULL;
1658 *pStart = EndBuffer;
1661 *pStart = s; /* remember the position for the next run */
1664 /* sanitize our extracted token */
1665 dest->buf[len] = '\0';
1666 dest->BufUsed = len;
1673 * @ingroup StrBuf_NextTokenizer
1674 * @brief a string tokenizer
1675 * @param Source StringBuffer to read from
1676 * @param pStart pointer to the end of the last token. Feed with NULL.
1677 * @param separator tokenizer character
1678 * @param nTokens number of tokens to fastforward over
1679 * @returns -1 if not found, else length of token.
1681 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1683 const char *s, *EndBuffer; //* source * /
1684 int len = 0; //* running total length of extracted string * /
1685 int current_token = 0; //* token currently being processed * /
1687 if ((Source == NULL) ||
1688 (Source->BufUsed ==0)) {
1692 return Source->BufUsed;
1694 if (*pStart == NULL)
1695 *pStart = Source->buf;
1697 EndBuffer = Source->buf + Source->BufUsed;
1699 if ((*pStart < Source->buf) ||
1700 (*pStart > EndBuffer)) {
1708 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1710 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1711 if (*s == separator) {
1714 if (current_token >= nTokens) {
1726 * @ingroup StrBuf_NextTokenizer
1727 * @brief a string tokenizer to fetch an integer
1728 * @param Source StringBuffer to read from
1729 * @param pStart Cursor on the tokenstring
1730 * @param separator tokenizer character
1731 * @returns 0 if not found, else integer representation of the token
1733 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1743 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1750 * @ingroup StrBuf_NextTokenizer
1751 * @brief a string tokenizer to fetch a long integer
1752 * @param Source StringBuffer to read from
1753 * @param pStart Cursor on the tokenstring
1754 * @param separator tokenizer character
1755 * @returns 0 if not found, else long integer representation of the token
1757 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1767 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1775 * @ingroup StrBuf_NextTokenizer
1776 * @brief a string tokenizer to fetch an unsigned long
1777 * @param Source StringBuffer to read from
1778 * @param pStart Cursor on the tokenstring
1779 * @param separator tokenizer character
1780 * @returns 0 if not found, else unsigned long representation of the token
1782 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1793 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1797 return (unsigned long) atol(pnum);
1807 /*******************************************************************************
1808 * Escape Appending *
1809 *******************************************************************************/
1812 * @ingroup StrBuf_DeEnCoder
1813 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1814 * @param OutBuf the output buffer
1815 * @param In Buffer to encode
1816 * @param PlainIn way in from plain old c strings
1818 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1820 const char *pch, *pche;
1824 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1826 if (PlainIn != NULL) {
1827 len = strlen(PlainIn);
1833 pche = pch + In->BufUsed;
1840 pt = OutBuf->buf + OutBuf->BufUsed;
1841 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1843 while (pch < pche) {
1845 IncreaseBuf(OutBuf, 1, -1);
1846 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1847 pt = OutBuf->buf + OutBuf->BufUsed;
1850 if((*pch >= 'a' && *pch <= 'z') ||
1851 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1852 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1853 (*pch == '!') || (*pch == '_') ||
1854 (*pch == ',') || (*pch == '.'))
1861 *(pt + 1) = HexList[(unsigned char)*pch][0];
1862 *(pt + 2) = HexList[(unsigned char)*pch][1];
1864 OutBuf->BufUsed += 3;
1872 * @ingroup StrBuf_DeEnCoder
1873 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1874 * @param OutBuf the output buffer
1875 * @param In Buffer to encode
1876 * @param PlainIn way in from plain old c strings
1878 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1880 const char *pch, *pche;
1884 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1886 if (PlainIn != NULL) {
1887 len = strlen(PlainIn);
1893 pche = pch + In->BufUsed;
1900 pt = OutBuf->buf + OutBuf->BufUsed;
1901 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1903 while (pch < pche) {
1905 IncreaseBuf(OutBuf, 1, -1);
1906 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1907 pt = OutBuf->buf + OutBuf->BufUsed;
1910 if((*pch >= 'a' && *pch <= 'z') ||
1911 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1912 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1913 (*pch == '!') || (*pch == '_') ||
1914 (*pch == ',') || (*pch == '.'))
1921 *(pt + 1) = HexList[(unsigned char)*pch][0];
1922 *(pt + 2) = HexList[(unsigned char)*pch][1];
1924 OutBuf->BufUsed += 3;
1932 * @ingroup StrBuf_DeEnCoder
1933 * @brief append a string with characters having a special meaning in xml encoded to the buffer
1934 * @param OutBuf the output buffer
1935 * @param In Buffer to encode
1936 * @param PlainIn way in from plain old c strings
1937 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1938 * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1940 void StrBufXMLEscAppend(StrBuf *OutBuf,
1942 const char *PlainIn,
1944 int OverrideLowChars)
1946 const char *pch, *pche;
1951 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1953 if (PlainIn != NULL) {
1955 len = strlen((const char*)PlainIn);
1962 pch = (const char*)In->buf;
1963 pche = pch + In->BufUsed;
1970 pt = OutBuf->buf + OutBuf->BufUsed;
1971 /**< we max append 6 chars at once plus the \0 */
1972 pte = OutBuf->buf + OutBuf->BufSize - 6;
1974 while (pch < pche) {
1976 OutBuf->BufUsed = pt - OutBuf->buf;
1977 IncreaseBuf(OutBuf, 1, -1);
1978 pte = OutBuf->buf + OutBuf->BufSize - 6;
1979 /**< we max append 3 chars at once plus the \0 */
1981 pt = OutBuf->buf + OutBuf->BufUsed;
1985 memcpy(pt, HKEY("<"));
1989 else if (*pch == '>') {
1990 memcpy(pt, HKEY(">"));
1994 else if (*pch == '&') {
1995 memcpy(pt, HKEY("&"));
1999 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
2003 else if (*pch < 0x20) {
2004 /* we probably shouldn't be doing this */
2005 if (OverrideLowChars)
2015 *pt = HexList[*(unsigned char*)pch][0];
2017 *pt = HexList[*(unsigned char*)pch][1];
2025 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
2028 while (IsUtf8Sequence > 0){
2039 *pt = HexList[*(unsigned char*)pch][0];
2041 *pt = HexList[*(unsigned char*)pch][1];
2050 OutBuf->BufUsed = pt - OutBuf->buf;
2055 * @ingroup StrBuf_DeEnCoder
2056 * @brief append a string in hex encoding to the buffer
2057 * @param OutBuf the output buffer
2058 * @param In Buffer to encode
2059 * @param PlainIn way in from plain old c strings
2060 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2062 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2064 const unsigned char *pch, *pche;
2068 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2070 if (PlainIn != NULL) {
2072 len = strlen((const char*)PlainIn);
2079 pch = (const unsigned char*)In->buf;
2080 pche = pch + In->BufUsed;
2087 pt = OutBuf->buf + OutBuf->BufUsed;
2088 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2090 while (pch < pche) {
2092 IncreaseBuf(OutBuf, 1, -1);
2093 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2094 pt = OutBuf->buf + OutBuf->BufUsed;
2097 *pt = HexList[*pch][0];
2099 *pt = HexList[*pch][1];
2100 pt ++; pch ++; OutBuf->BufUsed += 2;
2105 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2112 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2114 if (PlainIn != NULL) {
2116 len = strlen(PlainIn);
2129 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2131 if (ExpectLen > OutBuf->BufSize)
2132 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2135 pt = OutBuf->buf + OutBuf->BufUsed;
2137 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2140 OutBuf->BufUsed += len;
2145 * @ingroup StrBuf_DeEnCoder
2146 * @brief append a string in hex encoding to the buffer
2147 * @param OutBuf the output buffer
2148 * @param In Buffer to encode
2149 * @param PlainIn way in from plain old c strings
2151 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2153 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2157 * @ingroup StrBuf_DeEnCoder
2158 * @brief Append a string, escaping characters which have meaning in HTML.
2160 * @param Target target buffer
2161 * @param Source source buffer; set to NULL if you just have a C-String
2162 * @param PlainIn Plain-C string to append; set to NULL if unused
2163 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2164 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2165 * if set to 2, linebreaks are replaced by <br/>
2167 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2169 const char *aptr, *eiptr;
2173 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2176 if (PlainIn != NULL) {
2178 len = strlen(PlainIn);
2183 eiptr = aptr + Source->BufUsed;
2184 len = Source->BufUsed;
2190 bptr = Target->buf + Target->BufUsed;
2191 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2193 while (aptr < eiptr){
2195 IncreaseBuf(Target, 1, -1);
2196 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2197 bptr = Target->buf + Target->BufUsed;
2200 memcpy(bptr, "<", 4);
2202 Target->BufUsed += 4;
2204 else if (*aptr == '>') {
2205 memcpy(bptr, ">", 4);
2207 Target->BufUsed += 4;
2209 else if (*aptr == '&') {
2210 memcpy(bptr, "&", 5);
2212 Target->BufUsed += 5;
2214 else if (*aptr == '"') {
2215 memcpy(bptr, """, 6);
2217 Target->BufUsed += 6;
2219 else if (*aptr == '\'') {
2220 memcpy(bptr, "'", 5);
2222 Target->BufUsed += 5;
2224 else if (*aptr == LB) {
2229 else if (*aptr == RB) {
2234 else if (*aptr == QU) {
2239 else if ((*aptr == 32) && (nbsp == 1)) {
2240 memcpy(bptr, " ", 6);
2242 Target->BufUsed += 6;
2244 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2245 *bptr='\0'; /* nothing */
2247 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2248 memcpy(bptr, "<br/>", 11);
2250 Target->BufUsed += 11;
2254 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2255 *bptr='\0'; /* nothing */
2265 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2267 return Target->BufUsed;
2271 * @ingroup StrBuf_DeEnCoder
2272 * @brief Append a string, escaping characters which have meaning in HTML.
2273 * Converts linebreaks into blanks; escapes single quotes
2274 * @param Target target buffer
2275 * @param Source source buffer; set to NULL if you just have a C-String
2276 * @param PlainIn Plain-C string to append; set to NULL if unused
2278 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2280 const char *aptr, *eiptr;
2284 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2287 if (PlainIn != NULL) {
2289 len = strlen(PlainIn);
2294 eiptr = aptr + Source->BufUsed;
2295 len = Source->BufUsed;
2301 eptr = Target->buf + Target->BufSize - 8;
2302 tptr = Target->buf + Target->BufUsed;
2304 while (aptr < eiptr){
2306 IncreaseBuf(Target, 1, -1);
2307 eptr = Target->buf + Target->BufSize - 8;
2308 tptr = Target->buf + Target->BufUsed;
2311 if (*aptr == '\n') {
2315 else if (*aptr == '\r') {
2319 else if (*aptr == '\'') {
2325 Target->BufUsed += 5;
2338 * @ingroup StrBuf_DeEnCoder
2339 * @brief Append a string, escaping characters which have meaning in ICAL.
2341 * @param Target target buffer
2342 * @param Source source buffer; set to NULL if you just have a C-String
2343 * @param PlainIn Plain-C string to append; set to NULL if unused
2345 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2347 const char *aptr, *eiptr;
2351 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2354 if (PlainIn != NULL) {
2356 len = strlen(PlainIn);
2361 eiptr = aptr + Source->BufUsed;
2362 len = Source->BufUsed;
2368 eptr = Target->buf + Target->BufSize - 8;
2369 tptr = Target->buf + Target->BufUsed;
2371 while (aptr < eiptr){
2372 if(tptr + 3 >= eptr) {
2373 IncreaseBuf(Target, 1, -1);
2374 eptr = Target->buf + Target->BufSize - 8;
2375 tptr = Target->buf + Target->BufUsed;
2378 if (*aptr == '\n') {
2385 else if (*aptr == '\r') {
2392 else if (*aptr == ',') {
2408 * @ingroup StrBuf_DeEnCoder
2409 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2411 * @param Target target buffer
2412 * @param Source source buffer; set to NULL if you just have a C-String
2413 * @param PlainIn Plain-C string to append; set to NULL if unused
2414 * @returns size of result or -1
2416 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2418 const char *aptr, *eiptr;
2423 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2426 if (PlainIn != NULL) {
2428 len = strlen(PlainIn);
2433 eiptr = aptr + Source->BufUsed;
2434 len = Source->BufUsed;
2440 bptr = Target->buf + Target->BufUsed;
2441 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2443 while (aptr < eiptr){
2445 IncreaseBuf(Target, 1, -1);
2446 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2447 bptr = Target->buf + Target->BufUsed;
2451 memcpy(bptr, HKEY("\\n"));
2453 Target->BufUsed += 2;
2456 memcpy(bptr, HKEY("\\r"));
2458 Target->BufUsed += 2;
2465 Target->BufUsed += 2;
2468 if ((*(aptr + 1) == 'u') &&
2469 isxdigit(*(aptr + 2)) &&
2470 isxdigit(*(aptr + 3)) &&
2471 isxdigit(*(aptr + 4)) &&
2472 isxdigit(*(aptr + 5)))
2473 { /* oh, a unicode escaper. let it pass through. */
2474 memcpy(bptr, aptr, 6);
2477 Target->BufUsed += 6;
2485 Target->BufUsed += 2;
2493 Target->BufUsed += 2;
2500 Target->BufUsed += 2;
2507 Target->BufUsed += 2;
2510 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2511 while (IsUtf8Sequence > 0){
2514 if (--IsUtf8Sequence)
2522 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2524 return Target->BufUsed;
2528 * @ingroup StrBuf_DeEnCoder
2529 * @brief Append a string, escaping characters which have meaning in HTML + json.
2531 * @param Target target buffer
2532 * @param Source source buffer; set to NULL if you just have a C-String
2533 * @param PlainIn Plain-C string to append; set to NULL if unused
2534 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2535 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2536 * if set to 2, linebreaks are replaced by <br/>
2538 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2540 const char *aptr, *eiptr;
2543 int IsUtf8Sequence = 0;
2545 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2548 if (PlainIn != NULL) {
2550 len = strlen(PlainIn);
2555 eiptr = aptr + Source->BufUsed;
2556 len = Source->BufUsed;
2562 bptr = Target->buf + Target->BufUsed;
2563 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2565 while (aptr < eiptr){
2567 IncreaseBuf(Target, 1, -1);
2568 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2569 bptr = Target->buf + Target->BufUsed;
2573 memcpy(bptr, HKEY("<"));
2575 Target->BufUsed += 4;
2578 memcpy(bptr, HKEY(">"));
2580 Target->BufUsed += 4;
2583 memcpy(bptr, HKEY("&"));
2585 Target->BufUsed += 5;
2598 switch (nolinebreaks) {
2600 *bptr='\0'; /* nothing */
2603 memcpy(bptr, HKEY("<br/>"));
2605 Target->BufUsed += 11;
2608 memcpy(bptr, HKEY("\\n"));
2610 Target->BufUsed += 2;
2614 switch (nolinebreaks) {
2617 *bptr='\0'; /* nothing */
2620 memcpy(bptr, HKEY("\\r"));
2622 Target->BufUsed += 2;
2632 Target->BufUsed += 2;
2635 if ((*(aptr + 1) == 'u') &&
2636 isxdigit(*(aptr + 2)) &&
2637 isxdigit(*(aptr + 3)) &&
2638 isxdigit(*(aptr + 4)) &&
2639 isxdigit(*(aptr + 5)))
2640 { /* oh, a unicode escaper. let it pass through. */
2641 memcpy(bptr, aptr, 6);
2644 Target->BufUsed += 6;
2652 Target->BufUsed += 2;
2660 Target->BufUsed += 2;
2667 Target->BufUsed += 2;
2674 Target->BufUsed += 2;
2678 memcpy(bptr, HKEY(" "));
2680 Target->BufUsed += 6;
2684 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2685 while (IsUtf8Sequence > 0){
2688 if (--IsUtf8Sequence)
2696 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2698 return Target->BufUsed;
2703 * @ingroup StrBuf_DeEnCoder
2704 * @brief replace all non-Ascii characters by another
2705 * @param Buf buffer to inspect
2706 * @param repl charater to stamp over non ascii chars
2708 void StrBufAsciify(StrBuf *Buf, const char repl)
2712 for (offset = 0; offset < Buf->BufUsed; offset ++)
2713 if (!isascii(Buf->buf[offset]))
2714 Buf->buf[offset] = repl;
2719 * @ingroup StrBuf_DeEnCoder
2720 * @brief unhide special chars hidden to the HTML escaper
2721 * @param target buffer to put the unescaped string in
2722 * @param source buffer to unescape
2724 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2729 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2735 FlushStrBuf(target);
2737 len = source->BufUsed;
2738 for (a = 0; a < len; ++a) {
2739 if (target->BufUsed >= target->BufSize)
2740 IncreaseBuf(target, 1, -1);
2742 if (source->buf[a] == '=') {
2743 hex[0] = source->buf[a + 1];
2744 hex[1] = source->buf[a + 2];
2747 sscanf(hex, "%02x", &b);
2748 target->buf[target->BufUsed] = b;
2749 target->buf[++target->BufUsed] = 0;
2753 target->buf[target->BufUsed] = source->buf[a];
2754 target->buf[++target->BufUsed] = 0;
2761 * @ingroup StrBuf_DeEnCoder
2762 * @brief hide special chars from the HTML escapers and friends
2763 * @param target buffer to put the escaped string in
2764 * @param source buffer to escape
2766 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2771 FlushStrBuf(target);
2773 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2778 len = source->BufUsed;
2779 for (i=0; i<len; ++i) {
2780 if (target->BufUsed + 4 >= target->BufSize)
2781 IncreaseBuf(target, 1, -1);
2782 if ( (isalnum(source->buf[i])) ||
2783 (source->buf[i]=='-') ||
2784 (source->buf[i]=='_') ) {
2785 target->buf[target->BufUsed++] = source->buf[i];
2788 sprintf(&target->buf[target->BufUsed],
2790 (0xFF &source->buf[i]));
2791 target->BufUsed += 3;
2794 target->buf[target->BufUsed + 1] = '\0';
2798 /*******************************************************************************
2799 * Quoted Printable de/encoding *
2800 *******************************************************************************/
2803 * @ingroup StrBuf_DeEnCoder
2804 * @brief decode a buffer from base 64 encoding; destroys original
2805 * @param Buf Buffor to transform
2807 int StrBufDecodeBase64(StrBuf *Buf)
2815 xferbuf = (char*) malloc(Buf->BufSize);
2816 if (xferbuf == NULL)
2820 siz = CtdlDecodeBase64(xferbuf,
2830 * @ingroup StrBuf_DeEnCoder
2831 * @brief decode a buffer from base 64 encoding; expects targetbuffer
2832 * @param BufIn Buffor to transform
2833 * @param BufOut Buffer to put result into
2835 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2837 if ((BufIn == NULL) || (BufOut == NULL))
2840 if (BufOut->BufSize < BufIn->BufUsed)
2841 IncreaseBuf(BufOut, BufIn->BufUsed, 0);
2843 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2846 return BufOut->BufUsed;
2849 void *StrBufNewStreamContext(eStreamType type)
2851 base64_decodestate *state;;
2856 state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2857 base64_init_decodestate(state);
2864 void StrBufDestroyStreamContext(eStreamType type, void **Stream)
2874 void StrBufStreamDecodeTo(StrBuf *Target, const StrBuf *In, const char* pIn, long pInLen, void *Stream)
2876 base64_decodestate *state = Stream;
2882 pInLen = In->BufUsed;
2884 if ((pIn == NULL) || (Stream == NULL))
2887 ExpectLen = (pInLen / 4) * 3;
2889 if (Target->BufSize - Target->BufUsed < ExpectLen)
2891 IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
2894 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
2895 Target->BufUsed += ExpectLen;
2896 Target->buf[Target->BufUsed] = '\0';
2900 * @ingroup StrBuf_DeEnCoder
2901 * @brief decode a buffer from base 64 encoding; destroys original
2902 * @param Buf Buffor to transform
2904 int StrBufDecodeHex(StrBuf *Buf)
2907 char *pch, *pche, *pchi;
2909 if (Buf == NULL) return -1;
2911 pch = pchi = Buf->buf;
2912 pche = pch + Buf->BufUsed;
2914 while (pchi < pche){
2915 ch = decode_hex(pchi);
2922 Buf->BufUsed = pch - Buf->buf;
2923 return Buf->BufUsed;
2927 * @ingroup StrBuf_DeEnCoder
2928 * @brief replace all chars >0x20 && < 0x7F with Mute
2929 * @param Mute char to put over invalid chars
2930 * @param Buf Buffor to transform
2932 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2936 if (Buf == NULL) return -1;
2937 pch = (unsigned char *)Buf->buf;
2938 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2939 if ((*pch < 0x20) || (*pch > 0x7F))
2943 return Buf->BufUsed;
2948 * @ingroup StrBuf_DeEnCoder
2949 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2950 * @param Buf Buffer to translate
2951 * @param StripBlanks Reduce several blanks to one?
2953 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2962 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2963 Buf->buf[Buf->BufUsed - 1] = '\0';
2968 while (a < Buf->BufUsed) {
2969 if (Buf->buf[a] == '+')
2971 else if (Buf->buf[a] == '%') {
2972 /* don't let % chars through, rather truncate the input. */
2973 if (a + 2 > Buf->BufUsed) {
2978 hex[0] = Buf->buf[a + 1];
2979 hex[1] = Buf->buf[a + 2];
2982 sscanf(hex, "%02x", &b);
2983 Buf->buf[a] = (char) b;
2984 len = Buf->BufUsed - a - 2;
2986 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2998 * @ingroup StrBuf_DeEnCoder
2999 * @brief RFC2047-encode a header field if necessary.
3000 * If no non-ASCII characters are found, the string
3001 * will be copied verbatim without encoding.
3003 * @param target Target buffer.
3004 * @param source Source string to be encoded.
3005 * @returns encoded length; -1 if non success.
3007 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3009 const char headerStr[] = "=?UTF-8?Q?";
3010 int need_to_encode = 0;
3014 if ((source == NULL) ||
3018 while ((i < source->BufUsed) &&
3019 (!IsEmptyStr (&source->buf[i])) &&
3020 (need_to_encode == 0)) {
3021 if (((unsigned char) source->buf[i] < 32) ||
3022 ((unsigned char) source->buf[i] > 126)) {
3028 if (!need_to_encode) {
3029 if (*target == NULL) {
3030 *target = NewStrBufPlain(source->buf, source->BufUsed);
3033 FlushStrBuf(*target);
3034 StrBufAppendBuf(*target, source, 0);
3037 return (*target)->BufUsed;
3041 if (*target == NULL)
3042 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3043 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3044 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3045 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3046 (*target)->BufUsed = sizeof(headerStr) - 1;
3047 for (i=0; (i < source->BufUsed); ++i) {
3048 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3049 IncreaseBuf(*target, 1, 0);
3050 ch = (unsigned char) source->buf[i];
3059 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3060 (*target)->BufUsed += 3;
3064 (*target)->buf[(*target)->BufUsed] = '_';
3066 (*target)->buf[(*target)->BufUsed] = ch;
3067 (*target)->BufUsed++;
3071 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3072 IncreaseBuf(*target, 1, 0);
3074 (*target)->buf[(*target)->BufUsed++] = '?';
3075 (*target)->buf[(*target)->BufUsed++] = '=';
3076 (*target)->buf[(*target)->BufUsed] = '\0';
3077 return (*target)->BufUsed;;
3081 * @ingroup StrBuf_DeEnCoder
3082 * @brief Quoted-Printable encode a message; make it < 80 columns width.
3083 * @param source Source string to be encoded.
3084 * @returns buffer with encoded message.
3086 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3090 const char *ptr, *eptr;
3094 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3096 OEptr = OutBuf->buf + OutBuf->BufSize;
3097 ptr = EncodeMe->buf;
3098 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3103 if (Optr + 4 >= OEptr)
3106 Offset = Optr - OutBuf->buf;
3107 OutBuf->BufUsed = Optr - OutBuf->buf;
3108 IncreaseBuf(OutBuf, 1, 0);
3109 Optr = OutBuf->buf + Offset;
3110 OEptr = OutBuf->buf + OutBuf->BufSize;
3114 /* ignore carriage returns */
3117 else if (*ptr == '\n') {
3118 /* hard line break */
3119 memcpy(Optr, HKEY("=0A"));
3124 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3125 ( (*ptr >= 62) && (*ptr <= 126) ))
3136 *Optr = HexList[ch][0];
3138 *Optr = HexList[ch][1];
3145 /* soft line break */
3146 if (isspace(*(Optr - 1))) {
3151 *Optr = HexList[ch][0];
3153 *Optr = HexList[ch][1];
3165 OutBuf->BufUsed = Optr - OutBuf->buf;
3171 static void AddRecipient(StrBuf *Target,
3173 StrBuf *EmailAddress,
3178 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3179 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3181 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3182 StrBufRFC2047encode(&EncBuf, UserName);
3183 StrBufAppendBuf(Target, EncBuf, 0);
3184 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3185 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3187 if (StrLength(EmailAddress) > 0){
3188 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3189 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3190 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3196 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3197 * \param Recp Source list of email recipients
3198 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3199 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3200 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3201 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3203 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3205 StrBuf *EmailAddress,
3209 const char *pch, *pche;
3210 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3212 if ((Recp == NULL) || (StrLength(Recp) == 0))
3216 pche = pch + StrLength(Recp);
3218 if (!CheckEncode(pch, -1, pche))
3219 return NewStrBufDup(Recp);
3221 Target = NewStrBufPlain(NULL, StrLength(Recp));
3223 while ((pch != NULL) && (pch < pche))
3225 while (isspace(*pch)) pch++;
3226 UserEnd = EmailStart = EmailEnd = NULL;
3228 if ((*pch == '"') || (*pch == '\'')) {
3229 UserStart = pch + 1;
3231 UserEnd = strchr(UserStart, *pch);
3232 if (UserEnd == NULL)
3233 break; ///TODO: Userfeedback??
3234 EmailStart = UserEnd + 1;
3235 while (isspace(*EmailStart))
3237 if (UserEnd == UserStart) {
3238 UserStart = UserEnd = NULL;
3241 if (*EmailStart == '<') {
3243 EmailEnd = strchr(EmailStart, '>');
3244 if (EmailEnd == NULL)
3245 EmailEnd = strchr(EmailStart, ',');
3249 EmailEnd = strchr(EmailStart, ',');
3251 if (EmailEnd == NULL)
3258 EmailEnd = strchr(UserStart, ',');
3259 if (EmailEnd == NULL) {
3260 EmailEnd = strchr(pch, '>');
3262 if (EmailEnd != NULL) {
3272 while ((EmailEnd > UserStart) && !gt &&
3273 ((*EmailEnd == ',') ||
3274 (*EmailEnd == '>') ||
3275 (isspace(*EmailEnd))))
3277 if (*EmailEnd == '>')
3282 if (EmailEnd == UserStart)
3286 EmailStart = strchr(UserStart, '<');
3287 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3289 UserEnd = EmailStart;
3291 while ((UserEnd > UserStart) &&
3292 isspace (*(UserEnd - 1)))
3295 if (UserStart >= UserEnd)
3296 UserStart = UserEnd = NULL;
3298 else { /* this is a local recipient... no domain, just a realname */
3299 EmailStart = UserStart;
3300 At = strchr(EmailStart, '@');
3306 EmailStart = UserStart;
3312 if ((UserStart != NULL) && (UserEnd != NULL))
3313 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3314 else if ((UserStart != NULL) && (UserEnd == NULL))
3315 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3317 FlushStrBuf(UserName);
3319 if ((EmailStart != NULL) && (EmailEnd != NULL))
3320 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3321 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3322 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3324 FlushStrBuf(EmailAddress);
3326 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3331 if ((pch != NULL) && (*pch == ','))
3333 if (pch != NULL) while (isspace(*pch))
3342 * @brief replaces all occurances of 'search' by 'replace'
3343 * @param buf Buffer to modify
3344 * @param search character to search
3345 * @param replace character to replace search by
3347 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3352 for (i=0; i<buf->BufUsed; i++)
3353 if (buf->buf[i] == search)
3354 buf->buf[i] = replace;
3360 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3361 * @param buf Buffer to modify
3363 void StrBufToUnixLF(StrBuf *buf)
3365 char *pche, *pchS, *pchT;
3369 pche = buf->buf + buf->BufUsed;
3370 pchS = pchT = buf->buf;
3376 if (*pchS != '\n') {
3385 buf->BufUsed = pchT - buf->buf;
3389 /*******************************************************************************
3390 * Iconv Wrapper; RFC822 de/encoding *
3391 *******************************************************************************/
3394 * @ingroup StrBuf_DeEnCoder
3395 * @brief Wrapper around iconv_open()
3396 * Our version adds aliases for non-standard Microsoft charsets
3397 * such as 'MS950', aliasing them to names like 'CP950'
3399 * @param tocode Target encoding
3400 * @param fromcode Source encoding
3401 * @param pic anonimized pointer to iconv struct
3403 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3406 iconv_t ic = (iconv_t)(-1) ;
3407 ic = iconv_open(tocode, fromcode);
3408 if (ic == (iconv_t)(-1) ) {
3409 char alias_fromcode[64];
3410 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3411 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3412 alias_fromcode[0] = 'C';
3413 alias_fromcode[1] = 'P';
3414 ic = iconv_open(tocode, alias_fromcode);
3417 *(iconv_t *)pic = ic;
3423 * @ingroup StrBuf_DeEnCoder
3424 * @brief find one chunk of a RFC822 encoded string
3425 * @param Buffer where to search
3426 * @param bptr where to start searching
3427 * @returns found position, NULL if none.
3429 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3432 /* Find the next ?Q? */
3433 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3436 end = strchr(bptr + 2, '?');
3441 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3442 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3443 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3444 (*(end + 2) == '?')) {
3445 /* skip on to the end of the cluster, the next ?= */
3446 end = strstr(end + 3, "?=");
3449 /* sort of half valid encoding, try to find an end. */
3450 end = strstr(bptr, "?=");
3457 * @ingroup StrBuf_DeEnCoder
3458 * @brief convert one buffer according to the preselected iconv pointer PIC
3459 * @param ConvertBuf buffer we need to translate
3460 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3461 * @param pic Pointer to the iconv-session Object
3463 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3469 char *ibuf; /**< Buffer of characters to be converted */
3470 char *obuf; /**< Buffer for converted characters */
3471 size_t ibuflen; /**< Length of input buffer */
3472 size_t obuflen; /**< Length of output buffer */
3475 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3478 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3479 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3480 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3482 ic = *(iconv_t*)pic;
3483 ibuf = ConvertBuf->buf;
3484 ibuflen = ConvertBuf->BufUsed;
3486 obuflen = TmpBuf->BufSize;
3488 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3491 if (errno == E2BIG) {
3493 IncreaseBuf(TmpBuf, 0, 0);
3498 else if (errno == EILSEQ){
3499 /* hm, invalid utf8 sequence... what to do now? */
3500 /* An invalid multibyte sequence has been encountered in the input */
3502 else if (errno == EINVAL) {
3503 /* An incomplete multibyte sequence has been encountered in the input. */
3506 FlushStrBuf(TmpBuf);
3509 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3510 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3512 /* little card game: wheres the red lady? */
3513 SwapBuffers(ConvertBuf, TmpBuf);
3514 FlushStrBuf(TmpBuf);
3521 * @ingroup StrBuf_DeEnCoder
3522 * @brief catches one RFC822 encoded segment, and decodes it.
3523 * @param Target buffer to fill with result
3524 * @param DecodeMe buffer with stuff to process
3525 * @param SegmentStart points to our current segment in DecodeMe
3526 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3527 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3528 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3529 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3531 inline static void DecodeSegment(StrBuf *Target,
3532 const StrBuf *DecodeMe,
3533 const char *SegmentStart,
3534 const char *SegmentEnd,
3536 StrBuf *ConvertBuf2,
3537 StrBuf *FoundCharset)
3543 iconv_t ic = (iconv_t)(-1);
3547 /* Now we handle foreign character sets properly encoded
3548 * in RFC2047 format.
3550 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3551 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3552 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3553 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3554 if (FoundCharset != NULL) {
3555 FlushStrBuf(FoundCharset);
3556 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3558 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3559 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3561 *encoding = toupper(*encoding);
3562 if (*encoding == 'B') { /**< base64 */
3563 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3564 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3565 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3567 ConvertBuf->BufUsed);
3569 else if (*encoding == 'Q') { /**< quoted-printable */
3573 while (pos < ConvertBuf->BufUsed)
3575 if (ConvertBuf->buf[pos] == '_')
3576 ConvertBuf->buf[pos] = ' ';
3580 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3581 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3583 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3586 ConvertBuf->BufUsed);
3589 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3592 ctdl_iconv_open("UTF-8", charset, &ic);
3593 if (ic != (iconv_t)(-1) ) {
3595 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3596 StrBufAppendBuf(Target, ConvertBuf2, 0);
3601 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3607 * @ingroup StrBuf_DeEnCoder
3608 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3609 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3610 * @param Target where to put the decoded string to
3611 * @param DecodeMe buffer with encoded string
3612 * @param DefaultCharset if we don't find one, which should we use?
3613 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3614 * put it here for later use where no string might be known.
3616 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3619 StrBuf *ConvertBuf2;
3620 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3621 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3623 StrBuf_RFC822_2_Utf8(Target,
3629 FreeStrBuf(&ConvertBuf);
3630 FreeStrBuf(&ConvertBuf2);
3634 * @ingroup StrBuf_DeEnCoder
3635 * @brief Handle subjects with RFC2047 encoding such as:
3636 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3637 * @param Target where to put the decoded string to
3638 * @param DecodeMe buffer with encoded string
3639 * @param DefaultCharset if we don't find one, which should we use?
3640 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3641 * put it here for later use where no string might be known.
3642 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3643 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3645 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3646 const StrBuf *DecodeMe,
3647 const StrBuf* DefaultCharset,
3648 StrBuf *FoundCharset,
3650 StrBuf *ConvertBuf2)
3652 StrBuf *DecodedInvalidBuf = NULL;
3653 const StrBuf *DecodeMee = DecodeMe;
3654 const char *start, *end, *next, *nextend, *ptr = NULL;
3656 iconv_t ic = (iconv_t)(-1) ;
3661 int illegal_non_rfc2047_encoding = 0;
3664 if (DecodeMe == NULL)
3666 /* Sometimes, badly formed messages contain strings which were simply
3667 * written out directly in some foreign character set instead of
3668 * using RFC2047 encoding. This is illegal but we will attempt to
3669 * handle it anyway by converting from a user-specified default
3670 * charset to UTF-8 if we see any nonprintable characters.
3673 for (i=0; i<DecodeMe->BufUsed; ++i) {
3674 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3675 illegal_non_rfc2047_encoding = 1;
3680 if ((illegal_non_rfc2047_encoding) &&
3681 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3682 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3685 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3686 if (ic != (iconv_t)(-1) ) {
3687 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3688 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3689 DecodeMee = DecodedInvalidBuf;
3695 /* pre evaluate the first pair */
3697 start = strstr(DecodeMee->buf, "=?");
3698 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3700 end = FindNextEnd (DecodeMee, start + 2);
3702 StrBufAppendBuf(Target, DecodeMee, 0);
3703 FreeStrBuf(&DecodedInvalidBuf);
3708 if (start != DecodeMee->buf) {
3711 nFront = start - DecodeMee->buf;
3712 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3715 * Since spammers will go to all sorts of absurd lengths to get their
3716 * messages through, there are LOTS of corrupt headers out there.
3717 * So, prevent a really badly formed RFC2047 header from throwing
3718 * this function into an infinite loop.
3720 while ((start != NULL) &&
3727 DecodeSegment(Target,
3735 next = strstr(end, "=?");
3737 if ((next != NULL) &&
3739 nextend = FindNextEnd(DecodeMee, next);
3740 if (nextend == NULL)
3743 /* did we find two partitions */
3744 if ((next != NULL) &&
3748 while ((ptr < next) &&
3755 * did we find a gab just filled with blanks?
3756 * if not, copy its stuff over.
3760 StrBufAppendBufPlain(Target,
3766 /* our next-pair is our new first pair now. */
3772 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3773 if ((end != NULL) && (end < nextend)) {
3775 while ( (ptr < nextend) &&
3782 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3784 FreeStrBuf(&DecodedInvalidBuf);
3787 /*******************************************************************************
3788 * Manipulating UTF-8 Strings *
3789 *******************************************************************************/
3793 * @brief evaluate the length of an utf8 special character sequence
3794 * @param Char the character to examine
3795 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3797 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3800 unsigned char test = (1<<7);
3802 if ((*CharS & 0xC0) != 0xC0)
3806 ((test & ((unsigned char)*CharS)) != 0))
3811 if ((n > 6) || ((CharE - CharS) < n))
3818 * @brief detect whether this char starts an utf-8 encoded char
3819 * @param Char character to inspect
3820 * @returns yes or no
3822 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3824 /** 11??.???? indicates an UTF8 Sequence. */
3825 return ((Char & 0xC0) == 0xC0);
3830 * @brief measure the number of glyphs in an UTF8 string...
3831 * @param Buf string to measure
3832 * @returns the number of glyphs in Buf
3834 long StrBuf_Utf8StrLen(StrBuf *Buf)
3840 if ((Buf == NULL) || (Buf->BufUsed == 0))
3843 eptr = Buf->buf + Buf->BufUsed;
3844 while ((aptr < eptr) && (*aptr != '\0')) {
3845 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3846 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3847 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3860 * @brief cuts a string after maxlen glyphs
3861 * @param Buf string to cut to maxlen glyphs
3862 * @param maxlen how long may the string become?
3863 * @returns current length of the string
3865 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3871 eptr = Buf->buf + Buf->BufUsed;
3872 while ((aptr < eptr) && (*aptr != '\0')) {
3873 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3874 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3875 while ((*aptr++ != '\0') && (m-- > 0));
3884 Buf->BufUsed = aptr - Buf->buf;
3885 return Buf->BufUsed;
3888 return Buf->BufUsed;
3896 /*******************************************************************************
3898 *******************************************************************************/
3901 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3902 #define OS_CODE 0x03 /*< unix */
3905 * @ingroup StrBuf_DeEnCoder
3906 * @brief uses the same calling syntax as compress2(), but it
3907 * creates a stream compatible with HTTP "Content-encoding: gzip"
3908 * @param dest compressed buffer
3909 * @param destLen length of the compresed data
3910 * @param source source to encode
3911 * @param sourceLen length of source to encode
3912 * @param level compression level
3914 int ZEXPORT compress_gzip(Bytef * dest,
3916 const Bytef * source,
3920 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3922 /* write gzip header */
3923 snprintf((char *) dest, *destLen,
3924 "%c%c%c%c%c%c%c%c%c%c",
3925 gz_magic[0], gz_magic[1], Z_DEFLATED,
3926 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3929 /* normal deflate */
3932 stream.next_in = (Bytef *) source;
3933 stream.avail_in = (uInt) sourceLen;
3934 stream.next_out = dest + 10L; // after header
3935 stream.avail_out = (uInt) * destLen;
3936 if ((uLong) stream.avail_out != *destLen)
3939 stream.zalloc = (alloc_func) 0;
3940 stream.zfree = (free_func) 0;
3941 stream.opaque = (voidpf) 0;
3943 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3944 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3948 err = deflate(&stream, Z_FINISH);
3949 if (err != Z_STREAM_END) {
3950 deflateEnd(&stream);
3951 return err == Z_OK ? Z_BUF_ERROR : err;
3953 *destLen = stream.total_out + 10L;
3955 /* write CRC and Length */
3956 uLong crc = crc32(0L, source, sourceLen);
3958 for (n = 0; n < 4; ++n, ++*destLen) {
3959 dest[*destLen] = (int) (crc & 0xff);
3962 uLong len = stream.total_in;
3963 for (n = 0; n < 4; ++n, ++*destLen) {
3964 dest[*destLen] = (int) (len & 0xff);
3967 err = deflateEnd(&stream);
3974 * @ingroup StrBuf_DeEnCoder
3975 * @brief compress the buffer with gzip
3976 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3977 * @param Buf buffer whose content is to be gzipped
3979 int CompressBuffer(StrBuf *Buf)
3982 char *compressed_data = NULL;
3983 size_t compressed_len, bufsize;
3986 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3987 compressed_data = malloc(compressed_len);
3989 if (compressed_data == NULL)
3991 /* Flush some space after the used payload so valgrind shuts up... */
3992 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3993 Buf->buf[Buf->BufUsed + i++] = '\0';
3994 if (compress_gzip((Bytef *) compressed_data,
3997 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4000 Buf->buf = compressed_data;
4001 Buf->BufUsed = compressed_len;
4002 Buf->BufSize = bufsize;
4003 /* Flush some space after the used payload so valgrind shuts up... */
4005 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4006 Buf->buf[Buf->BufUsed + i++] = '\0';
4009 free(compressed_data);
4011 #endif /* HAVE_ZLIB */
4015 /*******************************************************************************
4016 * File I/O; Callbacks to libevent *
4017 *******************************************************************************/
4019 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4024 if ((FB == NULL) || (FB->Buf == NULL))
4028 * check whether the read pointer is somewhere in a range
4029 * where a cut left is inexpensive
4032 if (FB->ReadWritePointer != NULL)
4036 already_read = FB->ReadWritePointer - FB->Buf->buf;
4037 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4039 if (already_read != 0) {
4042 unread = FB->Buf->BufUsed - already_read;
4044 /* else nothing to compact... */
4046 FB->ReadWritePointer = FB->Buf->buf;
4047 bufremain = FB->Buf->BufSize;
4049 else if ((unread < 64) ||
4050 (bufremain < already_read))
4053 * if its just a tiny bit remaining, or we run out of space...
4056 FB->Buf->BufUsed = unread;
4057 if (unread < already_read)
4058 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4060 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4061 FB->ReadWritePointer = FB->Buf->buf;
4062 bufremain = FB->Buf->BufSize - unread - 1;
4064 else if (bufremain < (FB->Buf->BufSize / 10))
4066 /* get a bigger buffer */
4068 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4070 FB->ReadWritePointer = FB->Buf->buf + unread;
4072 bufremain = FB->Buf->BufSize - unread - 1;
4073 /*TODO: special increase function that won't copy the already read! */
4076 else if (bufremain < 10) {
4077 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4079 FB->ReadWritePointer = FB->Buf->buf;
4081 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4086 FB->ReadWritePointer = FB->Buf->buf;
4087 bufremain = FB->Buf->BufSize - 1;
4090 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4093 FB->Buf->BufUsed += n;
4094 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4099 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4104 if ((FB == NULL) || (FB->Buf == NULL))
4107 if (FB->ReadWritePointer != NULL)
4109 WriteRemain = FB->Buf->BufUsed -
4110 (FB->ReadWritePointer -
4114 FB->ReadWritePointer = FB->Buf->buf;
4115 WriteRemain = FB->Buf->BufUsed;
4118 n = write(fd, FB->ReadWritePointer, WriteRemain);
4120 FB->ReadWritePointer += n;
4122 if (FB->ReadWritePointer ==
4123 FB->Buf->buf + FB->Buf->BufUsed)
4125 FlushStrBuf(FB->Buf);
4126 FB->ReadWritePointer = NULL;
4129 // check whether we've got something to write
4130 // get the maximum chunk plus the pointer we can send
4131 // write whats there
4132 // if not all was sent, remember the send pointer for the next time
4133 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4139 * @ingroup StrBuf_IO
4140 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4141 * @param LineBuf your line will be copied here.
4142 * @param FB BLOB with lines of text...
4143 * @param Ptr moved arround to keep the next-line across several iterations
4144 * has to be &NULL on start; will be &NotNULL on end of buffer
4145 * @returns size of copied buffer
4147 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4149 const char *aptr, *ptr, *eptr;
4152 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4156 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4157 FB->ReadWritePointer = StrBufNOTNULL;
4161 FlushStrBuf(LineBuf);
4162 if (FB->ReadWritePointer == NULL)
4163 ptr = aptr = FB->Buf->buf;
4165 ptr = aptr = FB->ReadWritePointer;
4167 optr = LineBuf->buf;
4168 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4169 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4171 while ((ptr <= eptr) &&
4178 LineBuf->BufUsed = optr - LineBuf->buf;
4179 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4180 optr = LineBuf->buf + LineBuf->BufUsed;
4181 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4186 if (optr > LineBuf->buf)
4188 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4189 LineBuf->BufUsed = optr - LineBuf->buf;
4191 if ((FB->ReadWritePointer != NULL) &&
4192 (FB->ReadWritePointer != FB->Buf->buf))
4194 /* Ok, the client application read all the data
4195 it was interested in so far. Since there is more to read,
4196 we now shrink the buffer, and move the rest over.
4198 StrBufCutLeft(FB->Buf,
4199 FB->ReadWritePointer - FB->Buf->buf);
4200 FB->ReadWritePointer = FB->Buf->buf;
4202 return eMustReadMore;
4205 LineBuf->BufUsed = optr - LineBuf->buf;
4207 if ((ptr <= eptr) && (*ptr == '\r'))
4209 if ((ptr <= eptr) && (*ptr == '\n'))
4213 FB->ReadWritePointer = ptr;
4216 FlushStrBuf(FB->Buf);
4217 FB->ReadWritePointer = NULL;
4220 return eReadSuccess;
4224 * @ingroup StrBuf_CHUNKED_IO
4225 * @brief check whether the chunk-buffer has more data waiting or not.
4226 * @param FB Chunk-Buffer to inspect
4228 eReadState StrBufCheckBuffer(IOBuffer *FB)
4232 if (FB->Buf->BufUsed == 0)
4233 return eReadSuccess;
4234 if (FB->ReadWritePointer == NULL)
4235 return eBufferNotEmpty;
4236 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4237 return eBufferNotEmpty;
4238 return eReadSuccess;
4241 long IOBufferStrLength(IOBuffer *FB)
4243 if ((FB == NULL) || (FB->Buf == NULL))
4245 if (FB->ReadWritePointer == NULL)
4246 return StrLength(FB->Buf);
4248 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4251 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
4253 memset(FDB, 0, sizeof(FDIOBuffer));
4255 FDB->SplicePipe[0] = -1;
4256 FDB->SplicePipe[1] = -1;
4259 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
4261 FDIOBufferFlush(FDB);
4263 FDB->TotalSendSize = TotalSendSize;
4264 if (TotalSendSize > 0)
4265 FDB->ChunkSize = TotalSendSize;
4268 TotalSendSize = SIZ * 10;
4269 FDB->ChunkSize = TotalSendSize;
4275 pipe(FDB->SplicePipe);
4278 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
4283 void FDIOBufferDelete(FDIOBuffer *FDB)
4288 if (FDB->SplicePipe[0] > 0)
4289 close(FDB->SplicePipe[0]);
4290 if (FDB->SplicePipe[1] > 0)
4291 close(FDB->SplicePipe[1]);
4295 FreeStrBuf(&FDB->ChunkBuffer);
4297 if (FDB->OtherFD > 0)
4298 close(FDB->OtherFD);
4299 FDIOBufferFlush(FDB);
4302 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4304 ssize_t sent, pipesize;
4306 if (FDB->TotalSendSize > 0)
4311 if (FDB->PipeSize == 0)
4313 pipesize = splice(FDB->OtherFD,
4314 &FDB->TotalSentAlready,
4317 FDB->ChunkSendRemain,
4322 *Err = strerror(errno);
4325 FDB->PipeSize = pipesize;
4327 sent = splice(FDB->SplicePipe[0],
4332 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4335 *Err = strerror(errno);
4338 FDB->PipeSize -= sent;
4339 FDB->ChunkSendRemain -= sent;
4348 pRead = FDB->ChunkBuffer->buf;
4349 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4351 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4353 FDB->ChunkBuffer->BufUsed += nRead;
4354 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4356 else if (nRead == 0) {}
4360 nRead = write(FDB->IOB->fd,
4361 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4362 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4365 FDB->TotalSentAlready += nRead;
4366 FDB->ChunkSendRemain -= nRead;
4367 return FDB->ChunkSendRemain;
4379 if (FDB->PipeSize == 0)
4381 pipesize = splice(FDB->OtherFD,
4382 &FDB->TotalSentAlready,
4390 *Err = strerror(errno);
4393 FDB->PipeSize = pipesize;
4397 sent = splice(FDB->SplicePipe[0],
4402 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4405 *Err = strerror(errno);
4408 FDB->PipeSize -= sent;
4409 FDB->ChunkSendRemain -= sent;
4418 pRead = FDB->ChunkBuffer->buf;
4419 while ((FDB->ChunkSendRemain == 0) &&
4420 (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
4423 FDB->TotalSentAlready = 0;
4424 nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
4426 FDB->ChunkBuffer->BufUsed += nRead;
4427 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4428 FDB->ChunkSendRemain += nRead;
4430 else if (nRead == 0)
4436 *Err = strerror(errno);
4441 nRead = write(FDB->IOB->fd,
4442 FDB->ChunkBuffer->buf + FDB->TotalSentAlready,
4443 FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
4446 FDB->TotalSentAlready += nRead;
4447 FDB->ChunkSendRemain -= nRead;
4448 if (FDB->ChunkSendRemain == 0)
4450 FDB->ChunkBuffer->BufUsed = 0;
4451 FDB->TotalSentAlready = 0;
4453 return FDB->ChunkSendRemain;
4462 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4464 ssize_t sent, pipesize;
4469 if (FDB->PipeSize == 0)
4471 pipesize = splice(FDB->IOB->fd,
4475 FDB->ChunkSendRemain,
4476 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4480 *Err = strerror(errno);
4483 FDB->PipeSize = pipesize;
4486 sent = splice(FDB->SplicePipe[0],
4489 &FDB->TotalSentAlready,
4491 SPLICE_F_MORE | SPLICE_F_MOVE);
4495 *Err = strerror(errno);
4498 FDB->PipeSize -= sent;
4499 FDB->ChunkSendRemain -= sent;
4505 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4510 FDB->ChunkBuffer->BufUsed = sent;
4512 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4513 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4515 *Err = strerror(errno);
4521 FDB->ChunkBuffer->BufUsed = 0;
4522 FDB->TotalSentAlready += sent;
4523 FDB->ChunkSendRemain -= sent;
4524 return FDB->ChunkSendRemain;
4526 else if (sent < 0) {
4527 *Err = strerror(errno);
4534 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4536 ssize_t sent, pipesize;
4541 if (FDB->PipeSize == 0)
4543 pipesize = splice(FDB->IOB->fd,
4544 &FDB->TotalReadAlready,
4547 FDB->ChunkSendRemain,
4548 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4552 *Err = strerror(errno);
4555 FDB->PipeSize = pipesize;
4558 sent = splice(FDB->SplicePipe[0],
4561 &FDB->TotalSentAlready,
4563 SPLICE_F_MORE | SPLICE_F_MOVE);
4567 *Err = strerror(errno);
4570 FDB->PipeSize -= sent;
4571 FDB->ChunkSendRemain -= sent;
4577 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4582 FDB->ChunkBuffer->BufUsed = sent;
4584 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4585 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4587 *Err = strerror(errno);
4593 FDB->ChunkBuffer->BufUsed = 0;
4594 FDB->TotalSentAlready += sent;
4595 FDB->ChunkSendRemain -= sent;
4596 return FDB->ChunkSendRemain;
4598 else if (sent < 0) {
4599 *Err = strerror(errno);
4606 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4612 int nSuccessLess = 0;
4616 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4617 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4619 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4620 (FDB->ChunkSendRemain > 0))
4623 tv.tv_sec = 1; /* selectresolution; */
4627 FD_SET(FDB->OtherFD, &rfds);
4628 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4629 *Error = strerror(errno);
4633 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4638 should_write = FDB->IOB->Buf->BufUsed -
4639 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4640 if (should_write > FDB->ChunkSendRemain)
4641 should_write = FDB->ChunkSendRemain;
4643 rlen = write(FDB->OtherFD,
4644 FDB->IOB->ReadWritePointer,
4647 *Error = strerror(errno);
4651 FDB->TotalSentAlready += rlen;
4652 FDB->IOB->ReadWritePointer += rlen;
4653 FDB->ChunkSendRemain -= rlen;
4655 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4657 FlushStrBuf(FDB->IOB->Buf);
4658 FDB->IOB->ReadWritePointer = NULL;
4661 if (FDB->ChunkSendRemain == 0)
4662 return eReadSuccess;
4664 return eMustReadMore;
4667 /*******************************************************************************
4668 * File I/O; Prefer buffered read since its faster! *
4669 *******************************************************************************/
4672 * @ingroup StrBuf_IO
4673 * @brief Read a line from socket
4674 * flushes and closes the FD on error
4675 * @param buf the buffer to get the input to
4676 * @param fd pointer to the filedescriptor to read
4677 * @param append Append to an existing string or replace?
4678 * @param Error strerror() on error
4679 * @returns numbers of chars read
4681 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4683 int len, rlen, slen;
4685 if ((buf == NULL) || (buf->buf == NULL)) {
4686 *Error = strerror(EINVAL);
4693 slen = len = buf->BufUsed;
4695 rlen = read(*fd, &buf->buf[len], 1);
4697 *Error = strerror(errno);
4704 if (buf->buf[len] == '\n')
4706 if (buf->buf[len] != '\r')
4708 if (len + 2 >= buf->BufSize) {
4710 buf->buf[len+1] = '\0';
4711 IncreaseBuf(buf, 1, -1);
4715 buf->buf[len] = '\0';
4720 * @ingroup StrBuf_BufferedIO
4721 * @brief Read a line from socket
4722 * flushes and closes the FD on error
4723 * @param Line the line to read from the fd / I/O Buffer
4724 * @param buf the buffer to get the input to
4725 * @param fd pointer to the filedescriptor to read
4726 * @param timeout number of successless selects until we bail out
4727 * @param selectresolution how long to wait on each select
4728 * @param Error strerror() on error
4729 * @returns numbers of chars read
4731 int StrBufTCP_read_buffered_line(StrBuf *Line,
4735 int selectresolution,
4739 int nSuccessLess = 0;
4746 if (buf->BufUsed > 0) {
4747 pch = strchr(buf->buf, '\n');
4750 len = pch - buf->buf;
4751 if (len > 0 && (*(pch - 1) == '\r') )
4753 StrBufSub(Line, buf, 0, len - rlen);
4754 StrBufCutLeft(buf, len + 1);
4759 if (buf->BufSize - buf->BufUsed < 10)
4760 IncreaseBuf(buf, 1, -1);
4762 fdflags = fcntl(*fd, F_GETFL);
4763 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4765 while ((nSuccessLess < timeout) && (pch == NULL)) {
4767 tv.tv_sec = selectresolution;
4772 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4773 *Error = strerror(errno);
4779 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4784 &buf->buf[buf->BufUsed],
4785 buf->BufSize - buf->BufUsed - 1);
4787 *Error = strerror(errno);
4792 else if (rlen > 0) {
4794 buf->BufUsed += rlen;
4795 buf->buf[buf->BufUsed] = '\0';
4796 pch = strchr(buf->buf, '\n');
4797 if ((pch == NULL) &&
4798 (buf->BufUsed + 10 > buf->BufSize) &&
4799 (IncreaseBuf(buf, 1, -1) == -1))
4807 len = pch - buf->buf;
4808 if (len > 0 && (*(pch - 1) == '\r') )
4810 StrBufSub(Line, buf, 0, len - rlen);
4811 StrBufCutLeft(buf, len + 1);
4818 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4819 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4820 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4822 * @ingroup StrBuf_BufferedIO
4823 * @brief Read a line from socket
4824 * flushes and closes the FD on error
4825 * @param Line where to append our Line read from the fd / I/O Buffer;
4826 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4827 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4828 * @param fd pointer to the filedescriptor to read
4829 * @param timeout number of successless selects until we bail out
4830 * @param selectresolution how long to wait on each select
4831 * @param Error strerror() on error
4832 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4834 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4839 int selectresolution,
4842 const char *pche = NULL;
4843 const char *pos = NULL;
4845 int len, rlen, retlen;
4846 int nSuccessLess = 0;
4848 const char *pch = NULL;
4854 if ((Line == NULL) ||
4861 *Error = ErrRBLF_PreConditionFailed;
4866 if ((IOBuf->BufUsed > 0) &&
4868 (pos < IOBuf->buf + IOBuf->BufUsed))
4872 pche = IOBuf->buf + IOBuf->BufUsed;
4876 while ((pch < pche) && (*pch != '\n'))
4878 if (Line->BufUsed + 10 > Line->BufSize)
4881 apos = pcht - Line->buf;
4883 IncreaseBuf(Line, 1, -1);
4884 pcht = Line->buf + apos;
4892 if (len > 0 && (*(pch - 1) == '\r') )
4901 if ((pch >= pche) || (*pch == '\0'))
4909 if ((pch != NULL) &&
4912 if (pch + 1 >= pche) {
4925 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4927 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4928 IncreaseBuf(IOBuf, 1, -1);
4930 fdflags = fcntl(*fd, F_GETFL);
4931 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4934 while ((nSuccessLess < timeout) &&
4944 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4945 *Error = strerror(errno);
4949 *Error = ErrRBLF_SelectFailed;
4952 if (! FD_ISSET(*fd, &rfds) != 0) {
4958 &IOBuf->buf[IOBuf->BufUsed],
4959 IOBuf->BufSize - IOBuf->BufUsed - 1);
4961 *Error = strerror(errno);
4966 else if (rlen > 0) {
4968 pLF = IOBuf->buf + IOBuf->BufUsed;
4969 IOBuf->BufUsed += rlen;
4970 IOBuf->buf[IOBuf->BufUsed] = '\0';
4972 pche = IOBuf->buf + IOBuf->BufUsed;
4974 while ((pLF < pche) && (*pLF != '\n'))
4976 if ((pLF >= pche) || (*pLF == '\0'))
4979 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4983 if (pLF != NULL) apos = pLF - IOBuf->buf;
4984 IncreaseBuf(IOBuf, 1, -1);
4985 if (pLF != NULL) pLF = IOBuf->buf + apos;
4999 if (len > 0 && (*(pLF - 1) == '\r') )
5001 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
5002 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
5008 return retlen + len;
5010 *Error = ErrRBLF_NotEnoughSentFromServer;
5015 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
5017 * @ingroup StrBuf_IO
5018 * @brief Input binary data from socket
5019 * flushes and closes the FD on error
5020 * @param Buf the buffer to get the input to
5021 * @param fd pointer to the filedescriptor to read
5022 * @param append Append to an existing string or replace?
5023 * @param nBytes the maximal number of bytes to read
5024 * @param Error strerror() on error
5025 * @returns numbers of chars read
5027 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
5038 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
5040 *Error = ErrRBLF_BLOBPreConditionFailed;
5045 if (Buf->BufUsed + nBytes >= Buf->BufSize)
5046 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
5048 ptr = Buf->buf + Buf->BufUsed;
5050 fdflags = fcntl(*fd, F_GETFL);
5051 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5053 while ((nRead < nBytes) &&
5063 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5064 *Error = strerror(errno);
5068 *Error = ErrRBLF_SelectFailed;
5071 if (! FD_ISSET(*fd, &rfds) != 0) {
5077 if ((rlen = read(*fd,
5079 nBytes - nRead)) == -1) {
5082 *Error = strerror(errno);
5087 Buf->BufUsed += rlen;
5089 Buf->buf[Buf->BufUsed] = '\0';
5093 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
5094 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
5096 * @ingroup StrBuf_BufferedIO
5097 * @brief Input binary data from socket
5098 * flushes and closes the FD on error
5099 * @param Blob put binary thing here
5100 * @param IOBuf the buffer to get the input to
5101 * @param Pos offset inside of IOBuf
5102 * @param fd pointer to the filedescriptor to read
5103 * @param append Append to an existing string or replace?
5104 * @param nBytes the maximal number of bytes to read
5105 * @param check whether we should search for '000\n' terminators in case of timeouts
5106 * @param Error strerror() on error
5107 * @returns numbers of chars read
5109 int StrBufReadBLOBBuffered(StrBuf *Blob,
5122 int nAlreadyRead = 0;
5127 int nSuccessLess = 0;
5130 if ((Blob == NULL) ||
5137 *Error = ErrRBB_BLOBFPreConditionFailed;
5143 if (Blob->BufUsed + nBytes >= Blob->BufSize)
5144 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
5149 rlen = pos - IOBuf->buf;
5150 rlen = IOBuf->BufUsed - rlen;
5153 if ((IOBuf->BufUsed > 0) &&
5155 (pos < IOBuf->buf + IOBuf->BufUsed))
5157 if (rlen < nBytes) {
5158 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
5159 Blob->BufUsed += rlen;
5160 Blob->buf[Blob->BufUsed] = '\0';
5161 nAlreadyRead = nRead = rlen;
5164 if (rlen >= nBytes) {
5165 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
5166 Blob->BufUsed += nBytes;
5167 Blob->buf[Blob->BufUsed] = '\0';
5168 if (rlen == nBytes) {
5180 if (IOBuf->BufSize < nBytes - nRead)
5181 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5184 fdflags = fcntl(*fd, F_GETFL);
5185 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5193 while ((nSuccessLess < MaxTries) &&
5203 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5204 *Error = strerror(errno);
5208 *Error = ErrRBLF_SelectFailed;
5211 if (! FD_ISSET(*fd, &rfds) != 0) {
5218 IOBuf->BufSize - (ptr - IOBuf->buf));
5222 *Error = strerror(errno);
5225 else if (rlen == 0){
5226 if ((check == NNN_TERM) &&
5228 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
5230 StrBufPlain(Blob, HKEY("\n000\n"));
5231 StrBufCutRight(Blob, 5);
5232 return Blob->BufUsed;
5234 else if (!IsNonBlock)
5236 else if (nSuccessLess > MaxTries) {
5238 *Error = ErrRBB_too_many_selects;
5242 else if (rlen > 0) {
5246 IOBuf->BufUsed += rlen;
5249 if (nSuccessLess >= MaxTries) {
5251 *Error = ErrRBB_too_many_selects;
5255 if (nRead > nBytes) {
5256 *Pos = IOBuf->buf + nBytes;
5258 Blob->buf[Blob->BufUsed] = '\0';
5259 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5263 return nRead + nAlreadyRead;
5267 * @ingroup StrBuf_IO
5268 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5269 * @param LineBuf your line will be copied here.
5270 * @param Buf BLOB with lines of text...
5271 * @param Ptr moved arround to keep the next-line across several iterations
5272 * has to be &NULL on start; will be &NotNULL on end of buffer
5273 * @returns size of remaining buffer
5275 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5277 const char *aptr, *ptr, *eptr;
5280 if ((Buf == NULL) ||
5281 (*Ptr == StrBufNOTNULL) ||
5283 (LineBuf->buf == NULL))
5285 *Ptr = StrBufNOTNULL;
5289 FlushStrBuf(LineBuf);
5291 ptr = aptr = Buf->buf;
5295 optr = LineBuf->buf;
5296 eptr = Buf->buf + Buf->BufUsed;
5297 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5299 while ((ptr <= eptr) &&
5306 LineBuf->BufUsed = optr - LineBuf->buf;
5307 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
5308 optr = LineBuf->buf + LineBuf->BufUsed;
5309 xptr = LineBuf->buf + LineBuf->BufSize - 1;
5313 if ((ptr >= eptr) && (optr > LineBuf->buf))
5315 LineBuf->BufUsed = optr - LineBuf->buf;
5317 if ((ptr <= eptr) && (*ptr == '\r'))
5319 if ((ptr <= eptr) && (*ptr == '\n'))
5326 *Ptr = StrBufNOTNULL;
5329 return Buf->BufUsed - (ptr - Buf->buf);
5334 * @ingroup StrBuf_IO
5335 * @brief removes double slashes from pathnames
5336 * @param Dir directory string to filter
5337 * @param RemoveTrailingSlash allows / disallows trailing slashes
5339 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5345 while (!IsEmptyStr(a)) {
5357 if ((RemoveTrailingSlash) &&
5363 Dir->BufUsed = b - Dir->buf;