2 * Copyright (c) 1987-2011 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 #include <sys/select.h>
28 #include <sys/types.h>
29 #define SHOW_ME_VAPPEND_PRINTF
31 #ifndef LINUX_SENDFILE
32 #include <sys/sendfile.h>
34 #include "libcitadel.h"
45 #include <sys/sendfile.h>
50 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
51 const Bytef * source, uLong sourceLen, int level);
53 int BaseStrBufSize = 64;
55 const char *StrBufNOTNULL = ((char*) NULL) - 1;
57 const char HexList[256][3] = {
58 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
59 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
60 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
61 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
62 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
63 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
64 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
65 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
66 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
67 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
68 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
69 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
70 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
71 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
72 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
73 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
76 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
77 * StrBuf is a versatile class, aiding the handling of dynamic strings
78 * * reduce de/reallocations
79 * * reduce the need to remeasure it
80 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
81 * * allow asyncroneous IO for line and Blob based operations
82 * * reduce the use of memove in those
83 * * Quick filling in several operations with append functions
87 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
92 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
94 * use these operators to interfere with code demanding char*;
95 * if you need to own the content, smash me. Avoid, since we loose the length information.
99 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
101 * operations to get your Strings into a StrBuf, manipulating them, or appending
104 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
106 * Quick tokenizer; demands of the user to pull its tokens in sequence
110 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
112 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
116 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
118 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
119 * External Cursor to maintain the current read position inside of the buffer
120 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
124 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
130 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
132 * these functions translate the content of a buffer into another representation;
133 * some are combined Fillers and encoders
137 * Private Structure for the Stringbuffer
140 char *buf; /**< the pointer to the dynamic buffer */
141 long BufSize; /**< how many spcae do we optain */
142 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
143 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
145 long nIncreases; /**< for profiling; cound how many times we needed more */
146 char bt [SIZ]; /**< Stacktrace of last increase */
147 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
152 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
153 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
156 #ifdef HAVE_BACKTRACE
157 static void StrBufBacktrace(StrBuf *Buf, int which)
161 void *stack_frames[50];
166 pstart = pch = Buf->bt;
168 pstart = pch = Buf->bt_lastinc;
169 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
170 strings = backtrace_symbols(stack_frames, size);
171 for (i = 0; i < size; i++) {
173 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
175 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
184 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
186 if (hFreeDbglog == -1){
187 pid_t pid = getpid();
189 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
190 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
192 if ((*FreeMe)->nIncreases > 0)
196 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
198 (*FreeMe)->nIncreases,
202 (*FreeMe)->bt_lastinc);
203 n = write(hFreeDbglog, buf, n);
209 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
213 n = write(hFreeDbglog, buf, n);
217 void dbg_IncreaseBuf(StrBuf *IncMe)
220 #ifdef HAVE_BACKTRACE
221 StrBufBacktrace(Buf, 1);
225 void dbg_Init(StrBuf *Buf)
229 Buf->bt_lastinc[0] = '\0';
230 #ifdef HAVE_BACKTRACE
231 StrBufBacktrace(Buf, 0);
237 #define dbg_FreeStrBuf(a, b)
238 #define dbg_IncreaseBuf(a)
245 * @brief swaps the contents of two StrBufs
246 * this is to be used to have cheap switched between a work-buffer and a target buffer
248 * @param B second one
250 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
254 memcpy(&C, A, sizeof(*A));
255 memcpy(A, B, sizeof(*B));
256 memcpy(B, &C, sizeof(C));
261 * @ingroup StrBuf_Cast
262 * @brief Cast operator to Plain String
263 * @note if the buffer is altered by StrBuf operations, this pointer may become
264 * invalid. So don't lean on it after altering the buffer!
265 * Since this operation is considered cheap, rather call it often than risking
266 * your pointer to become invalid!
267 * @param Str the string we want to get the c-string representation for
268 * @returns the Pointer to the Content. Don't mess with it!
270 inline const char *ChrPtr(const StrBuf *Str)
278 * @ingroup StrBuf_Cast
279 * @brief since we know strlen()'s result, provide it here.
280 * @param Str the string to return the length to
281 * @returns contentlength of the buffer
283 inline int StrLength(const StrBuf *Str)
285 return (Str != NULL) ? Str->BufUsed : 0;
289 * @ingroup StrBuf_DeConstructors
290 * @brief local utility function to resize the buffer
291 * @param Buf the buffer whichs storage we should increase
292 * @param KeepOriginal should we copy the original buffer or just start over with a new one
293 * @param DestSize what should fit in after?
295 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
298 size_t NewSize = Buf->BufSize * 2;
304 while ((NewSize <= DestSize) && (NewSize != 0))
310 NewBuf= (char*) malloc(NewSize);
314 if (KeepOriginal && (Buf->BufUsed > 0))
316 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
325 Buf->BufSize = NewSize;
327 dbg_IncreaseBuf(Buf);
333 * @ingroup StrBuf_DeConstructors
334 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
335 * @param Buf Buffer to shrink (has to be empty)
336 * @param ThreshHold if the buffer is bigger then this, its readjusted
337 * @param NewSize if we Shrink it, how big are we going to be afterwards?
339 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
342 (Buf->BufUsed == 0) &&
343 (Buf->BufSize < ThreshHold)) {
345 Buf->buf = (char*) malloc(NewSize);
347 Buf->BufSize = NewSize;
352 * @ingroup StrBuf_DeConstructors
353 * @brief shrink long term buffers to their real size so they don't waste memory
354 * @param Buf buffer to shrink
355 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
356 * @returns physical size of the buffer
358 long StrBufShrinkToFit(StrBuf *Buf, int Force)
363 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
365 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
366 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
367 Buf->BufSize = Buf->BufUsed + 1;
375 * @ingroup StrBuf_DeConstructors
376 * @brief Allocate a new buffer with default buffer size
377 * @returns the new stringbuffer
379 StrBuf* NewStrBuf(void)
383 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
384 NewBuf->buf = (char*) malloc(BaseStrBufSize);
385 NewBuf->buf[0] = '\0';
386 NewBuf->BufSize = BaseStrBufSize;
388 NewBuf->ConstBuf = 0;
396 * @ingroup StrBuf_DeConstructors
397 * @brief Copy Constructor; returns a duplicate of CopyMe
398 * @param CopyMe Buffer to faxmilate
399 * @returns the new stringbuffer
401 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
408 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
409 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
410 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
411 NewBuf->BufUsed = CopyMe->BufUsed;
412 NewBuf->BufSize = CopyMe->BufSize;
413 NewBuf->ConstBuf = 0;
421 * @ingroup StrBuf_DeConstructors
422 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
423 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
424 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
425 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
426 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
427 * @returns the new stringbuffer
429 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
433 if (CreateRelpaceMe == NULL)
438 if (*CreateRelpaceMe != NULL)
439 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
441 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
445 if (CopyFlushMe == NULL)
447 if (*CreateRelpaceMe != NULL)
448 FlushStrBuf(*CreateRelpaceMe);
450 *CreateRelpaceMe = NewStrBuf();
455 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
456 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
457 * be a big IO-Buffer...
459 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
461 if (*CreateRelpaceMe == NULL)
463 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
468 NewBuf = *CreateRelpaceMe;
471 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
475 if (*CreateRelpaceMe == NULL)
477 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
481 NewBuf = *CreateRelpaceMe;
482 SwapBuffers (NewBuf, CopyFlushMe);
485 FlushStrBuf(CopyFlushMe);
490 * @ingroup StrBuf_DeConstructors
491 * @brief create a new Buffer using an existing c-string
492 * this function should also be used if you want to pre-suggest
493 * the buffer size to allocate in conjunction with ptr == NULL
494 * @param ptr the c-string to copy; may be NULL to create a blank instance
495 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
496 * @returns the new stringbuffer
498 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
501 size_t Siz = BaseStrBufSize;
504 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
506 CopySize = strlen((ptr != NULL)?ptr:"");
510 while ((Siz <= CopySize) && (Siz != 0))
518 NewBuf->buf = (char*) malloc(Siz);
519 if (NewBuf->buf == NULL)
524 NewBuf->BufSize = Siz;
526 memcpy(NewBuf->buf, ptr, CopySize);
527 NewBuf->buf[CopySize] = '\0';
528 NewBuf->BufUsed = CopySize;
531 NewBuf->buf[0] = '\0';
534 NewBuf->ConstBuf = 0;
542 * @ingroup StrBuf_DeConstructors
543 * @brief Set an existing buffer from a c-string
544 * @param Buf buffer to load
545 * @param ptr c-string to put into
546 * @param nChars set to -1 if we should work 0-terminated
547 * @returns the new length of the string
549 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
564 CopySize = strlen(ptr);
568 while ((Siz <= CopySize) && (Siz != 0))
576 if (Siz != Buf->BufSize)
577 IncreaseBuf(Buf, 0, Siz);
578 memcpy(Buf->buf, ptr, CopySize);
579 Buf->buf[CopySize] = '\0';
580 Buf->BufUsed = CopySize;
587 * @ingroup StrBuf_DeConstructors
588 * @brief use strbuf as wrapper for a string constant for easy handling
589 * @param StringConstant a string to wrap
590 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
592 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
596 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
597 NewBuf->buf = (char*) StringConstant;
598 NewBuf->BufSize = SizeOfStrConstant;
599 NewBuf->BufUsed = SizeOfStrConstant;
600 NewBuf->ConstBuf = 1;
609 * @ingroup StrBuf_DeConstructors
610 * @brief flush the content of a Buf; keep its struct
611 * @param buf Buffer to flush
613 int FlushStrBuf(StrBuf *buf)
625 * @ingroup StrBuf_DeConstructors
626 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
627 * @param buf Buffer to wipe
629 int FLUSHStrBuf(StrBuf *buf)
635 if (buf->BufUsed > 0) {
636 memset(buf->buf, 0, buf->BufUsed);
643 int hFreeDbglog = -1;
646 * @ingroup StrBuf_DeConstructors
647 * @brief Release a Buffer
648 * Its a double pointer, so it can NULL your pointer
649 * so fancy SIG11 appear instead of random results
650 * @param FreeMe Pointer Pointer to the buffer to free
652 void FreeStrBuf (StrBuf **FreeMe)
657 dbg_FreeStrBuf(FreeMe, 'F');
659 if (!(*FreeMe)->ConstBuf)
660 free((*FreeMe)->buf);
666 * @ingroup StrBuf_DeConstructors
667 * @brief flatten a Buffer to the Char * we return
668 * Its a double pointer, so it can NULL your pointer
669 * so fancy SIG11 appear instead of random results
670 * The Callee then owns the buffer and is responsible for freeing it.
671 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
672 * @returns the pointer of the buffer; Callee owns the memory thereafter.
674 char *SmashStrBuf (StrBuf **SmashMe)
678 if ((SmashMe == NULL) || (*SmashMe == NULL))
681 dbg_FreeStrBuf(SmashMe, 'S');
683 Ret = (*SmashMe)->buf;
690 * @ingroup StrBuf_DeConstructors
691 * @brief Release the buffer
692 * If you want put your StrBuf into a Hash, use this as Destructor.
693 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
695 void HFreeStrBuf (void *VFreeMe)
697 StrBuf *FreeMe = (StrBuf*)VFreeMe;
701 dbg_FreeStrBuf(SmashMe, 'H');
703 if (!FreeMe->ConstBuf)
709 /*******************************************************************************
710 * Simple string transformations *
711 *******************************************************************************/
715 * @brief Wrapper around atol
717 long StrTol(const StrBuf *Buf)
722 return atol(Buf->buf);
729 * @brief Wrapper around atoi
731 int StrToi(const StrBuf *Buf)
735 if (Buf->BufUsed > 0)
736 return atoi(Buf->buf);
743 * @brief Checks to see if the string is a pure number
744 * @param Buf The buffer to inspect
745 * @returns 1 if its a pure number, 0, if not.
747 int StrBufIsNumber(const StrBuf *Buf) {
749 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
752 strtoll(Buf->buf, &pEnd, 10);
753 if (pEnd == Buf->buf)
755 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
757 if (Buf->buf == pEnd)
763 * @ingroup StrBuf_Filler
764 * @brief modifies a Single char of the Buf
765 * You can point to it via char* or a zero-based integer
766 * @param Buf The buffer to manipulate
767 * @param ptr char* to zero; use NULL if unused
768 * @param nThChar zero based pointer into the string; use -1 if unused
769 * @param PeekValue The Character to place into the position
771 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
776 nThChar = ptr - Buf->buf;
777 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
779 Buf->buf[nThChar] = PeekValue;
784 * @ingroup StrBuf_Filler
785 * @brief modifies a range of chars of the Buf
786 * You can point to it via char* or a zero-based integer
787 * @param Buf The buffer to manipulate
788 * @param ptr char* to zero; use NULL if unused
789 * @param nThChar zero based pointer into the string; use -1 if unused
790 * @param nChars how many chars are to be flushed?
791 * @param PookValue The Character to place into that area
793 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
798 nThChar = ptr - Buf->buf;
799 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
801 if (nThChar + nChars > Buf->BufUsed)
802 nChars = Buf->BufUsed - nThChar;
804 memset(Buf->buf + nThChar, PookValue, nChars);
805 /* just to be shure... */
806 Buf->buf[Buf->BufUsed] = 0;
811 * @ingroup StrBuf_Filler
812 * @brief Append a StringBuffer to the buffer
813 * @param Buf Buffer to modify
814 * @param AppendBuf Buffer to copy at the end of our buffer
815 * @param Offset Should we start copying from an offset?
817 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
819 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
822 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
825 AppendBuf->BufUsed + Buf->BufUsed);
827 memcpy(Buf->buf + Buf->BufUsed,
828 AppendBuf->buf + Offset,
829 AppendBuf->BufUsed - Offset);
830 Buf->BufUsed += AppendBuf->BufUsed - Offset;
831 Buf->buf[Buf->BufUsed] = '\0';
836 * @ingroup StrBuf_Filler
837 * @brief Append a C-String to the buffer
838 * @param Buf Buffer to modify
839 * @param AppendBuf Buffer to copy at the end of our buffer
840 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
841 * @param Offset Should we start copying from an offset?
843 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
846 long BufSizeRequired;
848 if ((AppendBuf == NULL) || (Buf == NULL))
852 aps = strlen(AppendBuf + Offset);
854 aps = AppendSize - Offset;
856 BufSizeRequired = Buf->BufUsed + aps + 1;
857 if (Buf->BufSize <= BufSizeRequired)
858 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
860 memcpy(Buf->buf + Buf->BufUsed,
864 Buf->buf[Buf->BufUsed] = '\0';
868 * @ingroup StrBuf_Filler
869 * @brief sprintf like function appending the formated string to the buffer
870 * vsnprintf version to wrap into own calls
871 * @param Buf Buffer to extend by format and Params
872 * @param format printf alike format to add
873 * @param ap va_list containing the items for format
875 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
883 if ((Buf == NULL) || (format == NULL))
886 BufSize = Buf->BufSize;
887 nWritten = Buf->BufSize + 1;
888 Offset = Buf->BufUsed;
889 newused = Offset + nWritten;
891 while (newused >= BufSize) {
893 nWritten = vsnprintf(Buf->buf + Offset,
894 Buf->BufSize - Offset,
897 newused = Offset + nWritten;
898 if (newused >= Buf->BufSize) {
899 if (IncreaseBuf(Buf, 1, newused) == -1)
900 return; /* TODO: error handling? */
901 newused = Buf->BufSize + 1;
904 Buf->BufUsed = Offset + nWritten;
905 BufSize = Buf->BufSize;
912 * @ingroup StrBuf_Filler
913 * @brief sprintf like function appending the formated string to the buffer
914 * @param Buf Buffer to extend by format and Params
915 * @param format printf alike format to add
917 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
925 if ((Buf == NULL) || (format == NULL))
928 BufSize = Buf->BufSize;
929 nWritten = Buf->BufSize + 1;
930 Offset = Buf->BufUsed;
931 newused = Offset + nWritten;
933 while (newused >= BufSize) {
934 va_start(arg_ptr, format);
935 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
936 Buf->BufSize - Buf->BufUsed,
939 newused = Buf->BufUsed + nWritten;
940 if (newused >= Buf->BufSize) {
941 if (IncreaseBuf(Buf, 1, newused) == -1)
942 return; /* TODO: error handling? */
943 newused = Buf->BufSize + 1;
946 Buf->BufUsed += nWritten;
947 BufSize = Buf->BufSize;
954 * @ingroup StrBuf_Filler
955 * @brief sprintf like function putting the formated string into the buffer
956 * @param Buf Buffer to extend by format and Parameters
957 * @param format printf alike format to add
959 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
964 if ((Buf == NULL) || (format == NULL))
967 nWritten = Buf->BufSize + 1;
968 while (nWritten >= Buf->BufSize) {
969 va_start(arg_ptr, format);
970 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
972 if (nWritten >= Buf->BufSize) {
973 if (IncreaseBuf(Buf, 0, 0) == -1)
974 return; /* TODO: error handling? */
975 nWritten = Buf->BufSize + 1;
978 Buf->BufUsed = nWritten ;
983 * @ingroup StrBuf_Filler
984 * @brief Callback for cURL to append the webserver reply to a buffer
985 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
986 * @param size pre-defined by the cURL API; see man 3 curl for mre info
987 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
988 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
990 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
999 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1000 return size * nmemb;
1006 * @brief extracts a substring from Source into dest
1007 * @param dest buffer to place substring into
1008 * @param Source string to copy substring from
1009 * @param Offset chars to skip from start
1010 * @param nChars number of chars to copy
1011 * @returns the number of chars copied; may be different from nChars due to the size of Source
1013 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1015 size_t NCharsRemain;
1016 if (Offset > Source->BufUsed)
1022 if (Offset + nChars < Source->BufUsed)
1024 if (nChars >= dest->BufSize)
1025 IncreaseBuf(dest, 0, nChars + 1);
1026 memcpy(dest->buf, Source->buf + Offset, nChars);
1027 dest->BufUsed = nChars;
1028 dest->buf[dest->BufUsed] = '\0';
1031 NCharsRemain = Source->BufUsed - Offset;
1032 if (NCharsRemain >= dest->BufSize)
1033 IncreaseBuf(dest, 0, NCharsRemain + 1);
1034 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1035 dest->BufUsed = NCharsRemain;
1036 dest->buf[dest->BufUsed] = '\0';
1037 return NCharsRemain;
1042 * @brief Cut nChars from the start of the string
1043 * @param Buf Buffer to modify
1044 * @param nChars how many chars should be skipped?
1046 void StrBufCutLeft(StrBuf *Buf, int nChars)
1048 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1049 if (nChars >= Buf->BufUsed) {
1053 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1054 Buf->BufUsed -= nChars;
1055 Buf->buf[Buf->BufUsed] = '\0';
1060 * @brief Cut the trailing n Chars from the string
1061 * @param Buf Buffer to modify
1062 * @param nChars how many chars should be trunkated?
1064 void StrBufCutRight(StrBuf *Buf, int nChars)
1066 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1067 if (nChars >= Buf->BufUsed) {
1071 Buf->BufUsed -= nChars;
1072 Buf->buf[Buf->BufUsed] = '\0';
1077 * @brief Cut the string after n Chars
1078 * @param Buf Buffer to modify
1079 * @param AfternChars after how many chars should we trunkate the string?
1080 * @param At if non-null and points inside of our string, cut it there.
1082 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1084 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1086 AfternChars = At - Buf->buf;
1089 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1091 Buf->BufUsed = AfternChars;
1092 Buf->buf[Buf->BufUsed] = '\0';
1098 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1099 * @param Buf the string to modify
1101 void StrBufTrim(StrBuf *Buf)
1104 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1106 while ((Buf->BufUsed > 0) &&
1107 isspace(Buf->buf[Buf->BufUsed - 1]))
1111 Buf->buf[Buf->BufUsed] = '\0';
1113 if (Buf->BufUsed == 0) return;
1115 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1118 if (delta > 0) StrBufCutLeft(Buf, delta);
1122 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1123 * @param Buf the string to modify
1125 void StrBufSpaceToBlank(StrBuf *Buf)
1129 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1132 pche = pch + Buf->BufUsed;
1141 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1149 pLeft = pBuff = Buf->buf;
1150 while (pBuff != NULL) {
1152 pBuff = strchr(pBuff, leftboundary);
1161 pRight = strchr(pBuff, rightboundary);
1163 StrBufCutAt(Buf, 0, pRight);
1165 StrBufCutLeft(Buf, pLeft - Buf->buf);
1170 * @ingroup StrBuf_Filler
1171 * @brief uppercase the contents of a buffer
1172 * @param Buf the buffer to translate
1174 void StrBufUpCase(StrBuf *Buf)
1178 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1181 pche = pch + Buf->BufUsed;
1182 while (pch < pche) {
1183 *pch = toupper(*pch);
1190 * @ingroup StrBuf_Filler
1191 * @brief lowercase the contents of a buffer
1192 * @param Buf the buffer to translate
1194 void StrBufLowerCase(StrBuf *Buf)
1198 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1201 pche = pch + Buf->BufUsed;
1202 while (pch < pche) {
1203 *pch = tolower(*pch);
1209 /*******************************************************************************
1210 * a tokenizer that kills, maims, and destroys *
1211 *******************************************************************************/
1214 * @ingroup StrBuf_Tokenizer
1215 * @brief Replace a token at a given place with a given length by another token with given length
1216 * @param Buf String where to work on
1217 * @param where where inside of the Buf is the search-token
1218 * @param HowLong How long is the token to be replaced
1219 * @param Repl Token to insert at 'where'
1220 * @param ReplLen Length of repl
1221 * @returns -1 if fail else length of resulting Buf
1223 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1224 const char *Repl, long ReplLen)
1227 if ((Buf == NULL) ||
1228 (where > Buf->BufUsed) ||
1229 (where + HowLong > Buf->BufUsed))
1232 if (where + ReplLen - HowLong > Buf->BufSize)
1233 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1236 memmove(Buf->buf + where + ReplLen,
1237 Buf->buf + where + HowLong,
1238 Buf->BufUsed - where - HowLong);
1240 memcpy(Buf->buf + where,
1243 Buf->BufUsed += ReplLen - HowLong;
1245 return Buf->BufUsed;
1249 * @ingroup StrBuf_Tokenizer
1250 * @brief Counts the numbmer of tokens in a buffer
1251 * @param source String to count tokens in
1252 * @param tok Tokenizer char to count
1253 * @returns numbers of tokenizer chars found
1255 int StrBufNum_tokens(const StrBuf *source, char tok)
1259 if ((source == NULL) || (source->BufUsed == 0))
1261 if ((source->BufUsed == 1) && (*source->buf == tok))
1265 pche = pch + source->BufUsed;
1276 * @ingroup StrBuf_Tokenizer
1277 * @brief a string tokenizer
1278 * @param Source StringBuffer to read into
1279 * @param parmnum n'th Parameter to remove
1280 * @param separator tokenizer character
1281 * @returns -1 if not found, else length of token.
1283 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1286 char *d, *s, *end; /* dest, source */
1289 /* Find desired @parameter */
1290 end = Source->buf + Source->BufUsed;
1292 while ((d <= end) &&
1295 /* End of string, bail! */
1300 if (*d == separator) {
1305 if ((d == NULL) || (d >= end))
1306 return 0; /* @Parameter not found */
1308 /* Find next @parameter */
1310 while ((s <= end) &&
1311 (*s && *s != separator))
1315 if (*s == separator)
1319 /* Hack and slash */
1324 memmove(d, s, Source->BufUsed - (s - Source->buf));
1325 Source->BufUsed += ReducedBy;
1326 Source->buf[Source->BufUsed] = '\0';
1328 else if (d == Source->buf) {
1330 Source->BufUsed = 0;
1334 Source->BufUsed += ReducedBy;
1347 * @ingroup StrBuf_Tokenizer
1348 * @brief a string tokenizer
1349 * @param dest Destination StringBuffer
1350 * @param Source StringBuffer to read into
1351 * @param parmnum n'th Parameter to extract
1352 * @param separator tokenizer character
1353 * @returns -1 if not found, else length of token.
1355 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1357 const char *s, *e; //* source * /
1358 int len = 0; //* running total length of extracted string * /
1359 int current_token = 0; //* token currently being processed * /
1362 dest->buf[0] = '\0';
1368 if ((Source == NULL) || (Source->BufUsed ==0)) {
1372 e = s + Source->BufUsed;
1375 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1377 while ((s < e) && !IsEmptyStr(s)) {
1378 if (*s == separator) {
1381 if (len >= dest->BufSize) {
1382 dest->BufUsed = len;
1383 if (IncreaseBuf(dest, 1, -1) < 0) {
1388 if ( (current_token == parmnum) &&
1389 (*s != separator)) {
1390 dest->buf[len] = *s;
1393 else if (current_token > parmnum) {
1399 dest->buf[len] = '\0';
1400 dest->BufUsed = len;
1402 if (current_token < parmnum) {
1403 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1406 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1415 * @ingroup StrBuf_Tokenizer
1416 * @brief a string tokenizer to fetch an integer
1417 * @param Source String containing tokens
1418 * @param parmnum n'th Parameter to extract
1419 * @param separator tokenizer character
1420 * @returns 0 if not found, else integer representation of the token
1422 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1432 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1439 * @ingroup StrBuf_Tokenizer
1440 * @brief a string tokenizer to fetch a long integer
1441 * @param Source String containing tokens
1442 * @param parmnum n'th Parameter to extract
1443 * @param separator tokenizer character
1444 * @returns 0 if not found, else long integer representation of the token
1446 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1456 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1464 * @ingroup StrBuf_Tokenizer
1465 * @brief a string tokenizer to fetch an unsigned long
1466 * @param Source String containing tokens
1467 * @param parmnum n'th Parameter to extract
1468 * @param separator tokenizer character
1469 * @returns 0 if not found, else unsigned long representation of the token
1471 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1482 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1486 return (unsigned long) atol(pnum);
1495 * @ingroup StrBuf_NextTokenizer
1496 * @brief a string tokenizer; Bounds checker
1497 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1498 * @param Source our tokenbuffer
1499 * @param pStart the token iterator pointer to inspect
1500 * @returns whether the revolving pointer is inside of the search range
1502 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1504 if ((Source == NULL) ||
1505 (*pStart == StrBufNOTNULL) ||
1506 (Source->BufUsed == 0))
1510 if (*pStart == NULL)
1514 else if (*pStart > Source->buf + Source->BufUsed)
1518 else if (*pStart <= Source->buf)
1527 * @ingroup StrBuf_NextTokenizer
1528 * @brief a string tokenizer
1529 * @param dest Destination StringBuffer
1530 * @param Source StringBuffer to read into
1531 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1532 * @param separator tokenizer
1533 * @returns -1 if not found, else length of token.
1535 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1537 const char *s; /* source */
1538 const char *EndBuffer; /* end stop of source buffer */
1539 int current_token = 0; /* token currently being processed */
1540 int len = 0; /* running total length of extracted string */
1542 if ((Source == NULL) ||
1543 (Source->BufUsed == 0) )
1545 *pStart = StrBufNOTNULL;
1551 EndBuffer = Source->buf + Source->BufUsed;
1555 dest->buf[0] = '\0';
1560 *pStart = EndBuffer + 1;
1564 if (*pStart == NULL)
1566 *pStart = Source->buf; /* we're starting to examine this buffer. */
1568 else if ((*pStart < Source->buf) ||
1569 (*pStart > EndBuffer ) )
1571 return -1; /* no more tokens to find. */
1575 /* start to find the next token */
1576 while ((s <= EndBuffer) &&
1577 (current_token == 0) )
1579 if (*s == separator)
1581 /* we found the next token */
1585 if (len >= dest->BufSize)
1587 /* our Dest-buffer isn't big enough, increase it. */
1588 dest->BufUsed = len;
1590 if (IncreaseBuf(dest, 1, -1) < 0) {
1591 /* WHUT? no more mem? bail out. */
1598 if ( (current_token == 0 ) && /* are we in our target token? */
1599 (!IsEmptyStr(s) ) &&
1600 (separator != *s) ) /* don't copy the token itself */
1602 dest->buf[len] = *s; /* Copy the payload */
1603 ++len; /* remember the bigger size. */
1609 /* did we reach the end? */
1610 if ((s > EndBuffer)) {
1611 EndBuffer = StrBufNOTNULL;
1612 *pStart = EndBuffer;
1615 *pStart = s; /* remember the position for the next run */
1618 /* sanitize our extracted token */
1619 dest->buf[len] = '\0';
1620 dest->BufUsed = len;
1627 * @ingroup StrBuf_NextTokenizer
1628 * @brief a string tokenizer
1629 * @param Source StringBuffer to read from
1630 * @param pStart pointer to the end of the last token. Feed with NULL.
1631 * @param separator tokenizer character
1632 * @param nTokens number of tokens to fastforward over
1633 * @returns -1 if not found, else length of token.
1635 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1637 const char *s, *EndBuffer; //* source * /
1638 int len = 0; //* running total length of extracted string * /
1639 int current_token = 0; //* token currently being processed * /
1641 if ((Source == NULL) ||
1642 (Source->BufUsed ==0)) {
1646 return Source->BufUsed;
1648 if (*pStart == NULL)
1649 *pStart = Source->buf;
1651 EndBuffer = Source->buf + Source->BufUsed;
1653 if ((*pStart < Source->buf) ||
1654 (*pStart > EndBuffer)) {
1662 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1664 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1665 if (*s == separator) {
1668 if (current_token >= nTokens) {
1680 * @ingroup StrBuf_NextTokenizer
1681 * @brief a string tokenizer to fetch an integer
1682 * @param Source StringBuffer to read from
1683 * @param pStart Cursor on the tokenstring
1684 * @param separator tokenizer character
1685 * @returns 0 if not found, else integer representation of the token
1687 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1697 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1704 * @ingroup StrBuf_NextTokenizer
1705 * @brief a string tokenizer to fetch a long integer
1706 * @param Source StringBuffer to read from
1707 * @param pStart Cursor on the tokenstring
1708 * @param separator tokenizer character
1709 * @returns 0 if not found, else long integer representation of the token
1711 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1721 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1729 * @ingroup StrBuf_NextTokenizer
1730 * @brief a string tokenizer to fetch an unsigned long
1731 * @param Source StringBuffer to read from
1732 * @param pStart Cursor on the tokenstring
1733 * @param separator tokenizer character
1734 * @returns 0 if not found, else unsigned long representation of the token
1736 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1747 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1751 return (unsigned long) atol(pnum);
1761 /*******************************************************************************
1762 * Escape Appending *
1763 *******************************************************************************/
1766 * @ingroup StrBuf_DeEnCoder
1767 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1768 * @param OutBuf the output buffer
1769 * @param In Buffer to encode
1770 * @param PlainIn way in from plain old c strings
1772 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1774 const char *pch, *pche;
1778 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1780 if (PlainIn != NULL) {
1781 len = strlen(PlainIn);
1787 pche = pch + In->BufUsed;
1794 pt = OutBuf->buf + OutBuf->BufUsed;
1795 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1797 while (pch < pche) {
1799 IncreaseBuf(OutBuf, 1, -1);
1800 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1801 pt = OutBuf->buf + OutBuf->BufUsed;
1804 if((*pch >= 'a' && *pch <= 'z') ||
1805 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1806 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1807 (*pch == '!') || (*pch == '_') ||
1808 (*pch == ',') || (*pch == '.'))
1815 *(pt + 1) = HexList[(unsigned char)*pch][0];
1816 *(pt + 2) = HexList[(unsigned char)*pch][1];
1818 OutBuf->BufUsed += 3;
1826 * @ingroup StrBuf_DeEnCoder
1827 * @brief append a string in hex encoding to the buffer
1828 * @param OutBuf the output buffer
1829 * @param In Buffer to encode
1830 * @param PlainIn way in from plain old c strings
1831 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1833 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1835 const unsigned char *pch, *pche;
1839 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1841 if (PlainIn != NULL) {
1843 len = strlen((const char*)PlainIn);
1850 pch = (const unsigned char*)In->buf;
1851 pche = pch + In->BufUsed;
1858 pt = OutBuf->buf + OutBuf->BufUsed;
1859 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1861 while (pch < pche) {
1863 IncreaseBuf(OutBuf, 1, -1);
1864 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1865 pt = OutBuf->buf + OutBuf->BufUsed;
1868 *pt = HexList[*pch][0];
1870 *pt = HexList[*pch][1];
1871 pt ++; pch ++; OutBuf->BufUsed += 2;
1877 * @ingroup StrBuf_DeEnCoder
1878 * @brief append a string in hex encoding to the buffer
1879 * @param OutBuf the output buffer
1880 * @param In Buffer to encode
1881 * @param PlainIn way in from plain old c strings
1883 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1885 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1889 * @ingroup StrBuf_DeEnCoder
1890 * @brief Append a string, escaping characters which have meaning in HTML.
1892 * @param Target target buffer
1893 * @param Source source buffer; set to NULL if you just have a C-String
1894 * @param PlainIn Plain-C string to append; set to NULL if unused
1895 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1896 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1897 * if set to 2, linebreaks are replaced by <br/>
1899 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1901 const char *aptr, *eiptr;
1905 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1908 if (PlainIn != NULL) {
1910 len = strlen(PlainIn);
1915 eiptr = aptr + Source->BufUsed;
1916 len = Source->BufUsed;
1922 bptr = Target->buf + Target->BufUsed;
1923 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1925 while (aptr < eiptr){
1927 IncreaseBuf(Target, 1, -1);
1928 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1929 bptr = Target->buf + Target->BufUsed;
1932 memcpy(bptr, "<", 4);
1934 Target->BufUsed += 4;
1936 else if (*aptr == '>') {
1937 memcpy(bptr, ">", 4);
1939 Target->BufUsed += 4;
1941 else if (*aptr == '&') {
1942 memcpy(bptr, "&", 5);
1944 Target->BufUsed += 5;
1946 else if (*aptr == '"') {
1947 memcpy(bptr, """, 6);
1949 Target->BufUsed += 6;
1951 else if (*aptr == '\'') {
1952 memcpy(bptr, "'", 5);
1954 Target->BufUsed += 5;
1956 else if (*aptr == LB) {
1961 else if (*aptr == RB) {
1966 else if (*aptr == QU) {
1971 else if ((*aptr == 32) && (nbsp == 1)) {
1972 memcpy(bptr, " ", 6);
1974 Target->BufUsed += 6;
1976 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1977 *bptr='\0'; /* nothing */
1979 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1980 memcpy(bptr, "<br/>", 11);
1982 Target->BufUsed += 11;
1986 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1987 *bptr='\0'; /* nothing */
1997 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
1999 return Target->BufUsed;
2003 * @ingroup StrBuf_DeEnCoder
2004 * @brief Append a string, escaping characters which have meaning in HTML.
2005 * Converts linebreaks into blanks; escapes single quotes
2006 * @param Target target buffer
2007 * @param Source source buffer; set to NULL if you just have a C-String
2008 * @param PlainIn Plain-C string to append; set to NULL if unused
2010 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2012 const char *aptr, *eiptr;
2016 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2019 if (PlainIn != NULL) {
2021 len = strlen(PlainIn);
2026 eiptr = aptr + Source->BufUsed;
2027 len = Source->BufUsed;
2033 eptr = Target->buf + Target->BufSize - 8;
2034 tptr = Target->buf + Target->BufUsed;
2036 while (aptr < eiptr){
2038 IncreaseBuf(Target, 1, -1);
2039 eptr = Target->buf + Target->BufSize - 8;
2040 tptr = Target->buf + Target->BufUsed;
2043 if (*aptr == '\n') {
2047 else if (*aptr == '\r') {
2051 else if (*aptr == '\'') {
2057 Target->BufUsed += 5;
2070 * @ingroup StrBuf_DeEnCoder
2071 * @brief Append a string, escaping characters which have meaning in ICAL.
2073 * @param Target target buffer
2074 * @param Source source buffer; set to NULL if you just have a C-String
2075 * @param PlainIn Plain-C string to append; set to NULL if unused
2077 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2079 const char *aptr, *eiptr;
2083 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2086 if (PlainIn != NULL) {
2088 len = strlen(PlainIn);
2093 eiptr = aptr + Source->BufUsed;
2094 len = Source->BufUsed;
2100 eptr = Target->buf + Target->BufSize - 8;
2101 tptr = Target->buf + Target->BufUsed;
2103 while (aptr < eiptr){
2104 if(tptr + 3 >= eptr) {
2105 IncreaseBuf(Target, 1, -1);
2106 eptr = Target->buf + Target->BufSize - 8;
2107 tptr = Target->buf + Target->BufUsed;
2110 if (*aptr == '\n') {
2117 else if (*aptr == '\r') {
2124 else if (*aptr == ',') {
2140 * @ingroup StrBuf_DeEnCoder
2141 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2143 * @param Target target buffer
2144 * @param Source source buffer; set to NULL if you just have a C-String
2145 * @param PlainIn Plain-C string to append; set to NULL if unused
2146 * @returns size of result or -1
2148 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2150 const char *aptr, *eiptr;
2155 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2158 if (PlainIn != NULL) {
2160 len = strlen(PlainIn);
2165 eiptr = aptr + Source->BufUsed;
2166 len = Source->BufUsed;
2172 bptr = Target->buf + Target->BufUsed;
2173 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2175 while (aptr < eiptr){
2177 IncreaseBuf(Target, 1, -1);
2178 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2179 bptr = Target->buf + Target->BufUsed;
2183 memcpy(bptr, HKEY("\\n"));
2185 Target->BufUsed += 2;
2188 memcpy(bptr, HKEY("\\r"));
2190 Target->BufUsed += 2;
2197 Target->BufUsed += 2;
2200 if ((*(aptr + 1) == 'u') &&
2201 isxdigit(*(aptr + 2)) &&
2202 isxdigit(*(aptr + 3)) &&
2203 isxdigit(*(aptr + 4)) &&
2204 isxdigit(*(aptr + 5)))
2205 { /* oh, a unicode escaper. let it pass through. */
2206 memcpy(bptr, aptr, 6);
2209 Target->BufUsed += 6;
2217 Target->BufUsed += 2;
2225 Target->BufUsed += 2;
2232 Target->BufUsed += 2;
2239 Target->BufUsed += 2;
2242 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2243 while (IsUtf8Sequence > 0){
2246 if (--IsUtf8Sequence)
2254 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2256 return Target->BufUsed;
2260 * @ingroup StrBuf_DeEnCoder
2261 * @brief Append a string, escaping characters which have meaning in HTML + json.
2263 * @param Target target buffer
2264 * @param Source source buffer; set to NULL if you just have a C-String
2265 * @param PlainIn Plain-C string to append; set to NULL if unused
2266 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2267 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2268 * if set to 2, linebreaks are replaced by <br/>
2270 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2272 const char *aptr, *eiptr;
2275 int IsUtf8Sequence = 0;
2277 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2280 if (PlainIn != NULL) {
2282 len = strlen(PlainIn);
2287 eiptr = aptr + Source->BufUsed;
2288 len = Source->BufUsed;
2294 bptr = Target->buf + Target->BufUsed;
2295 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2297 while (aptr < eiptr){
2299 IncreaseBuf(Target, 1, -1);
2300 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2301 bptr = Target->buf + Target->BufUsed;
2305 memcpy(bptr, HKEY("<"));
2307 Target->BufUsed += 4;
2310 memcpy(bptr, HKEY(">"));
2312 Target->BufUsed += 4;
2315 memcpy(bptr, HKEY("&"));
2317 Target->BufUsed += 5;
2330 switch (nolinebreaks) {
2332 *bptr='\0'; /* nothing */
2335 memcpy(bptr, HKEY("<br/>"));
2337 Target->BufUsed += 11;
2340 memcpy(bptr, HKEY("\\n"));
2342 Target->BufUsed += 2;
2346 switch (nolinebreaks) {
2349 *bptr='\0'; /* nothing */
2352 memcpy(bptr, HKEY("\\r"));
2354 Target->BufUsed += 2;
2364 Target->BufUsed += 2;
2367 if ((*(aptr + 1) == 'u') &&
2368 isxdigit(*(aptr + 2)) &&
2369 isxdigit(*(aptr + 3)) &&
2370 isxdigit(*(aptr + 4)) &&
2371 isxdigit(*(aptr + 5)))
2372 { /* oh, a unicode escaper. let it pass through. */
2373 memcpy(bptr, aptr, 6);
2376 Target->BufUsed += 6;
2384 Target->BufUsed += 2;
2392 Target->BufUsed += 2;
2399 Target->BufUsed += 2;
2406 Target->BufUsed += 2;
2410 memcpy(bptr, HKEY(" "));
2412 Target->BufUsed += 6;
2416 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2417 while (IsUtf8Sequence > 0){
2420 if (--IsUtf8Sequence)
2428 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2430 return Target->BufUsed;
2434 * @ingroup StrBuf_DeEnCoder
2435 * @brief unhide special chars hidden to the HTML escaper
2436 * @param target buffer to put the unescaped string in
2437 * @param source buffer to unescape
2439 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2445 FlushStrBuf(target);
2447 if (source == NULL ||target == NULL)
2452 len = source->BufUsed;
2453 for (a = 0; a < len; ++a) {
2454 if (target->BufUsed >= target->BufSize)
2455 IncreaseBuf(target, 1, -1);
2457 if (source->buf[a] == '=') {
2458 hex[0] = source->buf[a + 1];
2459 hex[1] = source->buf[a + 2];
2462 sscanf(hex, "%02x", &b);
2463 target->buf[target->BufUsed] = b;
2464 target->buf[++target->BufUsed] = 0;
2468 target->buf[target->BufUsed] = source->buf[a];
2469 target->buf[++target->BufUsed] = 0;
2476 * @ingroup StrBuf_DeEnCoder
2477 * @brief hide special chars from the HTML escapers and friends
2478 * @param target buffer to put the escaped string in
2479 * @param source buffer to escape
2481 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2486 FlushStrBuf(target);
2488 if (source == NULL ||target == NULL)
2493 len = source->BufUsed;
2494 for (i=0; i<len; ++i) {
2495 if (target->BufUsed + 4 >= target->BufSize)
2496 IncreaseBuf(target, 1, -1);
2497 if ( (isalnum(source->buf[i])) ||
2498 (source->buf[i]=='-') ||
2499 (source->buf[i]=='_') ) {
2500 target->buf[target->BufUsed++] = source->buf[i];
2503 sprintf(&target->buf[target->BufUsed],
2505 (0xFF &source->buf[i]));
2506 target->BufUsed += 3;
2509 target->buf[target->BufUsed + 1] = '\0';
2513 /*******************************************************************************
2514 * Quoted Printable de/encoding *
2515 *******************************************************************************/
2518 * @ingroup StrBuf_DeEnCoder
2519 * @brief decode a buffer from base 64 encoding; destroys original
2520 * @param Buf Buffor to transform
2522 int StrBufDecodeBase64(StrBuf *Buf)
2526 if (Buf == NULL) return -1;
2528 xferbuf = (char*) malloc(Buf->BufSize);
2530 siz = CtdlDecodeBase64(xferbuf,
2540 * @ingroup StrBuf_DeEnCoder
2541 * @brief decode a buffer from base 64 encoding; destroys original
2542 * @param Buf Buffor to transform
2544 int StrBufDecodeHex(StrBuf *Buf)
2547 char *pch, *pche, *pchi;
2549 if (Buf == NULL) return -1;
2551 pch = pchi = Buf->buf;
2552 pche = pch + Buf->BufUsed;
2554 while (pchi < pche){
2555 ch = decode_hex(pchi);
2562 Buf->BufUsed = pch - Buf->buf;
2563 return Buf->BufUsed;
2567 * @ingroup StrBuf_DeEnCoder
2568 * @brief replace all chars >0x20 && < 0x7F with Mute
2569 * @param Mute char to put over invalid chars
2570 * @param Buf Buffor to transform
2572 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2576 if (Buf == NULL) return -1;
2577 pch = (unsigned char *)Buf->buf;
2578 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2579 if ((*pch < 0x20) || (*pch > 0x7F))
2583 return Buf->BufUsed;
2588 * @ingroup StrBuf_DeEnCoder
2589 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2590 * @param Buf Buffer to translate
2591 * @param StripBlanks Reduce several blanks to one?
2593 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2602 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2603 Buf->buf[Buf->BufUsed - 1] = '\0';
2608 while (a < Buf->BufUsed) {
2609 if (Buf->buf[a] == '+')
2611 else if (Buf->buf[a] == '%') {
2612 /* don't let % chars through, rather truncate the input. */
2613 if (a + 2 > Buf->BufUsed) {
2618 hex[0] = Buf->buf[a + 1];
2619 hex[1] = Buf->buf[a + 2];
2622 sscanf(hex, "%02x", &b);
2623 Buf->buf[a] = (char) b;
2624 len = Buf->BufUsed - a - 2;
2626 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2638 * @ingroup StrBuf_DeEnCoder
2639 * @brief RFC2047-encode a header field if necessary.
2640 * If no non-ASCII characters are found, the string
2641 * will be copied verbatim without encoding.
2643 * @param target Target buffer.
2644 * @param source Source string to be encoded.
2645 * @returns encoded length; -1 if non success.
2647 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2649 const char headerStr[] = "=?UTF-8?Q?";
2650 int need_to_encode = 0;
2654 if ((source == NULL) ||
2658 while ((i < source->BufUsed) &&
2659 (!IsEmptyStr (&source->buf[i])) &&
2660 (need_to_encode == 0)) {
2661 if (((unsigned char) source->buf[i] < 32) ||
2662 ((unsigned char) source->buf[i] > 126)) {
2668 if (!need_to_encode) {
2669 if (*target == NULL) {
2670 *target = NewStrBufPlain(source->buf, source->BufUsed);
2673 FlushStrBuf(*target);
2674 StrBufAppendBuf(*target, source, 0);
2676 return (*target)->BufUsed;
2678 if (*target == NULL)
2679 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2680 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2681 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2682 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2683 (*target)->BufUsed = sizeof(headerStr) - 1;
2684 for (i=0; (i < source->BufUsed); ++i) {
2685 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2686 IncreaseBuf(*target, 1, 0);
2687 ch = (unsigned char) source->buf[i];
2697 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2698 (*target)->BufUsed += 3;
2702 (*target)->buf[(*target)->BufUsed] = '_';
2704 (*target)->buf[(*target)->BufUsed] = ch;
2705 (*target)->BufUsed++;
2709 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2710 IncreaseBuf(*target, 1, 0);
2712 (*target)->buf[(*target)->BufUsed++] = '?';
2713 (*target)->buf[(*target)->BufUsed++] = '=';
2714 (*target)->buf[(*target)->BufUsed] = '\0';
2715 return (*target)->BufUsed;;
2720 static void AddRecipient(StrBuf *Target,
2722 StrBuf *EmailAddress,
2727 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2728 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2730 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2731 StrBufRFC2047encode(&EncBuf, UserName);
2732 StrBufAppendBuf(Target, EncBuf, 0);
2733 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2734 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2736 if (StrLength(EmailAddress) > 0){
2737 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2738 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2739 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2745 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2746 * \param Recp Source list of email recipients
2747 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2748 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2749 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2750 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2752 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2754 StrBuf *EmailAddress,
2758 const char *pch, *pche;
2759 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2761 if ((Recp == NULL) || (StrLength(Recp) == 0))
2765 pche = pch + StrLength(Recp);
2767 if (!CheckEncode(pch, -1, pche))
2768 return NewStrBufDup(Recp);
2770 Target = NewStrBufPlain(NULL, StrLength(Recp));
2772 while ((pch != NULL) && (pch < pche))
2774 while (isspace(*pch)) pch++;
2775 UserStart = UserEnd = EmailStart = EmailEnd = NULL;
2777 if ((*pch == '"') || (*pch == '\'')) {
2778 UserStart = pch + 1;
2780 UserEnd = strchr(UserStart, *pch);
2781 if (UserEnd == NULL)
2782 break; ///TODO: Userfeedback??
2783 EmailStart = UserEnd + 1;
2784 while (isspace(*EmailStart))
2786 if (UserEnd == UserStart) {
2787 UserStart = UserEnd = NULL;
2790 if (*EmailStart == '<') {
2792 EmailEnd = strchr(EmailStart, '>');
2793 if (EmailEnd == NULL)
2794 EmailEnd = strchr(EmailStart, ',');
2798 EmailEnd = strchr(EmailStart, ',');
2800 if (EmailEnd == NULL)
2807 EmailEnd = strchr(UserStart, ',');
2808 if (EmailEnd == NULL) {
2809 EmailEnd = strchr(pch, '>');
2811 if (EmailEnd != NULL) {
2821 while ((EmailEnd > UserStart) && !gt &&
2822 ((*EmailEnd == ',') ||
2823 (*EmailEnd == '>') ||
2824 (isspace(*EmailEnd))))
2826 if (*EmailEnd == '>')
2831 if (EmailEnd == UserStart)
2835 EmailStart = strchr(UserStart, '<');
2836 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2838 UserEnd = EmailStart;
2840 while ((UserEnd > UserStart) &&
2841 isspace (*(UserEnd - 1)))
2844 if (UserStart >= UserEnd)
2845 UserStart = UserEnd = NULL;
2846 At = strchr(EmailStart, '@');
2848 else { /* this is a local recipient... no domain, just a realname */
2849 EmailStart = UserStart;
2850 At = strchr(EmailStart, '@');
2856 EmailStart = UserStart;
2862 if ((UserStart != NULL) && (UserEnd != NULL))
2863 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2864 else if ((UserStart != NULL) && (UserEnd == NULL))
2865 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2867 FlushStrBuf(UserName);
2869 if ((EmailStart != NULL) && (EmailEnd != NULL))
2870 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2871 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2872 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2874 FlushStrBuf(EmailAddress);
2876 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2881 if ((pch != NULL) && (*pch == ','))
2883 if (pch != NULL) while (isspace(*pch))
2892 * @brief replaces all occurances of 'search' by 'replace'
2893 * @param buf Buffer to modify
2894 * @param search character to search
2895 * @param replace character to replace search by
2897 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2902 for (i=0; i<buf->BufUsed; i++)
2903 if (buf->buf[i] == search)
2904 buf->buf[i] = replace;
2910 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2911 * @param buf Buffer to modify
2913 void StrBufToUnixLF(StrBuf *buf)
2915 char *pche, *pchS, *pchT;
2919 pche = buf->buf + buf->BufUsed;
2920 pchS = pchT = buf->buf;
2926 if (*pchS != '\n') {
2935 buf->BufUsed = pchT - buf->buf;
2939 /*******************************************************************************
2940 * Iconv Wrapper; RFC822 de/encoding *
2941 *******************************************************************************/
2944 * @ingroup StrBuf_DeEnCoder
2945 * @brief Wrapper around iconv_open()
2946 * Our version adds aliases for non-standard Microsoft charsets
2947 * such as 'MS950', aliasing them to names like 'CP950'
2949 * @param tocode Target encoding
2950 * @param fromcode Source encoding
2951 * @param pic anonimized pointer to iconv struct
2953 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2956 iconv_t ic = (iconv_t)(-1) ;
2957 ic = iconv_open(tocode, fromcode);
2958 if (ic == (iconv_t)(-1) ) {
2959 char alias_fromcode[64];
2960 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2961 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2962 alias_fromcode[0] = 'C';
2963 alias_fromcode[1] = 'P';
2964 ic = iconv_open(tocode, alias_fromcode);
2967 *(iconv_t *)pic = ic;
2973 * @ingroup StrBuf_DeEnCoder
2974 * @brief find one chunk of a RFC822 encoded string
2975 * @param Buffer where to search
2976 * @param bptr where to start searching
2977 * @returns found position, NULL if none.
2979 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
2982 /* Find the next ?Q? */
2983 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
2986 end = strchr(bptr + 2, '?');
2991 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2992 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
2993 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
2994 (*(end + 2) == '?')) {
2995 /* skip on to the end of the cluster, the next ?= */
2996 end = strstr(end + 3, "?=");
2999 /* sort of half valid encoding, try to find an end. */
3000 end = strstr(bptr, "?=");
3007 * @ingroup StrBuf_DeEnCoder
3008 * @brief convert one buffer according to the preselected iconv pointer PIC
3009 * @param ConvertBuf buffer we need to translate
3010 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3011 * @param pic Pointer to the iconv-session Object
3013 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3019 char *ibuf; /**< Buffer of characters to be converted */
3020 char *obuf; /**< Buffer for converted characters */
3021 size_t ibuflen; /**< Length of input buffer */
3022 size_t obuflen; /**< Length of output buffer */
3025 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3026 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3027 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3029 ic = *(iconv_t*)pic;
3030 ibuf = ConvertBuf->buf;
3031 ibuflen = ConvertBuf->BufUsed;
3033 obuflen = TmpBuf->BufSize;
3035 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3038 if (errno == E2BIG) {
3040 IncreaseBuf(TmpBuf, 0, 0);
3045 else if (errno == EILSEQ){
3046 /* hm, invalid utf8 sequence... what to do now? */
3047 /* An invalid multibyte sequence has been encountered in the input */
3049 else if (errno == EINVAL) {
3050 /* An incomplete multibyte sequence has been encountered in the input. */
3053 FlushStrBuf(TmpBuf);
3056 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3057 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3059 /* little card game: wheres the red lady? */
3060 SwapBuffers(ConvertBuf, TmpBuf);
3061 FlushStrBuf(TmpBuf);
3068 * @ingroup StrBuf_DeEnCoder
3069 * @brief catches one RFC822 encoded segment, and decodes it.
3070 * @param Target buffer to fill with result
3071 * @param DecodeMe buffer with stuff to process
3072 * @param SegmentStart points to our current segment in DecodeMe
3073 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3074 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3075 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3076 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3078 inline static void DecodeSegment(StrBuf *Target,
3079 const StrBuf *DecodeMe,
3080 const char *SegmentStart,
3081 const char *SegmentEnd,
3083 StrBuf *ConvertBuf2,
3084 StrBuf *FoundCharset)
3090 iconv_t ic = (iconv_t)(-1);
3094 /* Now we handle foreign character sets properly encoded
3095 * in RFC2047 format.
3097 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3098 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3099 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3100 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3101 if (FoundCharset != NULL) {
3102 FlushStrBuf(FoundCharset);
3103 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3105 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3106 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3108 *encoding = toupper(*encoding);
3109 if (*encoding == 'B') { /**< base64 */
3110 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3111 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3112 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3114 ConvertBuf->BufUsed);
3116 else if (*encoding == 'Q') { /**< quoted-printable */
3120 while (pos < ConvertBuf->BufUsed)
3122 if (ConvertBuf->buf[pos] == '_')
3123 ConvertBuf->buf[pos] = ' ';
3127 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3128 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3130 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3133 ConvertBuf->BufUsed);
3136 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3139 ctdl_iconv_open("UTF-8", charset, &ic);
3140 if (ic != (iconv_t)(-1) ) {
3142 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3143 StrBufAppendBuf(Target, ConvertBuf2, 0);
3148 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3154 * @ingroup StrBuf_DeEnCoder
3155 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3156 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3157 * @param Target where to put the decoded string to
3158 * @param DecodeMe buffer with encoded string
3159 * @param DefaultCharset if we don't find one, which should we use?
3160 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3161 * put it here for later use where no string might be known.
3163 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3166 StrBuf *ConvertBuf2;
3167 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3168 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3170 StrBuf_RFC822_2_Utf8(Target,
3176 FreeStrBuf(&ConvertBuf);
3177 FreeStrBuf(&ConvertBuf2);
3181 * @ingroup StrBuf_DeEnCoder
3182 * @brief Handle subjects with RFC2047 encoding such as:
3183 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3184 * @param Target where to put the decoded string to
3185 * @param DecodeMe buffer with encoded string
3186 * @param DefaultCharset if we don't find one, which should we use?
3187 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3188 * put it here for later use where no string might be known.
3189 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3190 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3192 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3193 const StrBuf *DecodeMe,
3194 const StrBuf* DefaultCharset,
3195 StrBuf *FoundCharset,
3197 StrBuf *ConvertBuf2)
3199 StrBuf *DecodedInvalidBuf = NULL;
3200 const StrBuf *DecodeMee = DecodeMe;
3201 const char *start, *end, *next, *nextend, *ptr = NULL;
3203 iconv_t ic = (iconv_t)(-1) ;
3208 int illegal_non_rfc2047_encoding = 0;
3210 /* Sometimes, badly formed messages contain strings which were simply
3211 * written out directly in some foreign character set instead of
3212 * using RFC2047 encoding. This is illegal but we will attempt to
3213 * handle it anyway by converting from a user-specified default
3214 * charset to UTF-8 if we see any nonprintable characters.
3217 len = StrLength(DecodeMe);
3218 for (i=0; i<DecodeMe->BufUsed; ++i) {
3219 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3220 illegal_non_rfc2047_encoding = 1;
3225 if ((illegal_non_rfc2047_encoding) &&
3226 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3227 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3230 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3231 if (ic != (iconv_t)(-1) ) {
3232 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3233 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3234 DecodeMee = DecodedInvalidBuf;
3240 /* pre evaluate the first pair */
3241 nextend = end = NULL;
3242 len = StrLength(DecodeMee);
3243 start = strstr(DecodeMee->buf, "=?");
3244 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3246 end = FindNextEnd (DecodeMee, start + 2);
3248 StrBufAppendBuf(Target, DecodeMee, 0);
3249 FreeStrBuf(&DecodedInvalidBuf);
3254 if (start != DecodeMee->buf) {
3257 nFront = start - DecodeMee->buf;
3258 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3262 * Since spammers will go to all sorts of absurd lengths to get their
3263 * messages through, there are LOTS of corrupt headers out there.
3264 * So, prevent a really badly formed RFC2047 header from throwing
3265 * this function into an infinite loop.
3267 while ((start != NULL) &&
3274 DecodeSegment(Target,
3282 next = strstr(end, "=?");
3284 if ((next != NULL) &&
3286 nextend = FindNextEnd(DecodeMee, next);
3287 if (nextend == NULL)
3290 /* did we find two partitions */
3291 if ((next != NULL) &&
3295 while ((ptr < next) &&
3302 * did we find a gab just filled with blanks?
3303 * if not, copy its stuff over.
3307 StrBufAppendBufPlain(Target,
3313 /* our next-pair is our new first pair now. */
3319 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3320 if ((end != NULL) && (end < nextend)) {
3322 while ( (ptr < nextend) &&
3329 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3331 FreeStrBuf(&DecodedInvalidBuf);
3334 /*******************************************************************************
3335 * Manipulating UTF-8 Strings *
3336 *******************************************************************************/
3340 * @brief evaluate the length of an utf8 special character sequence
3341 * @param Char the character to examine
3342 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3344 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3347 unsigned char test = (1<<7);
3349 if ((*CharS & 0xC0) != 0xC0)
3353 ((test & ((unsigned char)*CharS)) != 0))
3358 if ((n > 6) || ((CharE - CharS) < n))
3365 * @brief detect whether this char starts an utf-8 encoded char
3366 * @param Char character to inspect
3367 * @returns yes or no
3369 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3371 /** 11??.???? indicates an UTF8 Sequence. */
3372 return ((Char & 0xC0) == 0xC0);
3377 * @brief measure the number of glyphs in an UTF8 string...
3378 * @param Buf string to measure
3379 * @returns the number of glyphs in Buf
3381 long StrBuf_Utf8StrLen(StrBuf *Buf)
3387 if ((Buf == NULL) || (Buf->BufUsed == 0))
3390 eptr = Buf->buf + Buf->BufUsed;
3391 while ((aptr < eptr) && (*aptr != '\0')) {
3392 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3393 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3394 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3407 * @brief cuts a string after maxlen glyphs
3408 * @param Buf string to cut to maxlen glyphs
3409 * @param maxlen how long may the string become?
3410 * @returns current length of the string
3412 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3418 eptr = Buf->buf + Buf->BufUsed;
3419 while ((aptr < eptr) && (*aptr != '\0')) {
3420 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3421 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3422 while ((*aptr++ != '\0') && (m-- > 0));
3431 Buf->BufUsed = aptr - Buf->buf;
3432 return Buf->BufUsed;
3435 return Buf->BufUsed;
3443 /*******************************************************************************
3445 *******************************************************************************/
3448 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3449 #define OS_CODE 0x03 /*< unix */
3452 * @ingroup StrBuf_DeEnCoder
3453 * @brief uses the same calling syntax as compress2(), but it
3454 * creates a stream compatible with HTTP "Content-encoding: gzip"
3455 * @param dest compressed buffer
3456 * @param destLen length of the compresed data
3457 * @param source source to encode
3458 * @param sourceLen length of source to encode
3459 * @param level compression level
3461 int ZEXPORT compress_gzip(Bytef * dest,
3463 const Bytef * source,
3467 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3469 /* write gzip header */
3470 snprintf((char *) dest, *destLen,
3471 "%c%c%c%c%c%c%c%c%c%c",
3472 gz_magic[0], gz_magic[1], Z_DEFLATED,
3473 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3476 /* normal deflate */
3479 stream.next_in = (Bytef *) source;
3480 stream.avail_in = (uInt) sourceLen;
3481 stream.next_out = dest + 10L; // after header
3482 stream.avail_out = (uInt) * destLen;
3483 if ((uLong) stream.avail_out != *destLen)
3486 stream.zalloc = (alloc_func) 0;
3487 stream.zfree = (free_func) 0;
3488 stream.opaque = (voidpf) 0;
3490 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3491 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3495 err = deflate(&stream, Z_FINISH);
3496 if (err != Z_STREAM_END) {
3497 deflateEnd(&stream);
3498 return err == Z_OK ? Z_BUF_ERROR : err;
3500 *destLen = stream.total_out + 10L;
3502 /* write CRC and Length */
3503 uLong crc = crc32(0L, source, sourceLen);
3505 for (n = 0; n < 4; ++n, ++*destLen) {
3506 dest[*destLen] = (int) (crc & 0xff);
3509 uLong len = stream.total_in;
3510 for (n = 0; n < 4; ++n, ++*destLen) {
3511 dest[*destLen] = (int) (len & 0xff);
3514 err = deflateEnd(&stream);
3521 * @ingroup StrBuf_DeEnCoder
3522 * @brief compress the buffer with gzip
3523 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3524 * @param Buf buffer whose content is to be gzipped
3526 int CompressBuffer(StrBuf *Buf)
3529 char *compressed_data = NULL;
3530 size_t compressed_len, bufsize;
3533 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3534 compressed_data = malloc(compressed_len);
3536 if (compressed_data == NULL)
3538 /* Flush some space after the used payload so valgrind shuts up... */
3539 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3540 Buf->buf[Buf->BufUsed + i++] = '\0';
3541 if (compress_gzip((Bytef *) compressed_data,
3544 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3547 Buf->buf = compressed_data;
3548 Buf->BufUsed = compressed_len;
3549 Buf->BufSize = bufsize;
3550 /* Flush some space after the used payload so valgrind shuts up... */
3552 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3553 Buf->buf[Buf->BufUsed + i++] = '\0';
3556 free(compressed_data);
3558 #endif /* HAVE_ZLIB */
3562 /*******************************************************************************
3563 * File I/O; Callbacks to libevent *
3564 *******************************************************************************/
3566 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3571 if ((FB == NULL) || (FB->Buf == NULL))
3575 * check whether the read pointer is somewhere in a range
3576 * where a cut left is inexpensive
3579 if (FB->ReadWritePointer != NULL)
3583 already_read = FB->ReadWritePointer - FB->Buf->buf;
3584 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3586 if (already_read != 0) {
3589 unread = FB->Buf->BufUsed - already_read;
3591 /* else nothing to compact... */
3593 FB->ReadWritePointer = FB->Buf->buf;
3594 bufremain = FB->Buf->BufSize;
3596 else if ((unread < 64) ||
3597 (bufremain < already_read))
3600 * if its just a tiny bit remaining, or we run out of space...
3603 FB->Buf->BufUsed = unread;
3604 if (unread < already_read)
3605 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3607 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3608 FB->ReadWritePointer = FB->Buf->buf;
3609 bufremain = FB->Buf->BufSize - unread - 1;
3611 else if (bufremain < (FB->Buf->BufSize / 10))
3613 /* get a bigger buffer */
3615 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3617 FB->ReadWritePointer = FB->Buf->buf + unread;
3619 bufremain = FB->Buf->BufSize - unread - 1;
3620 /*TODO: special increase function that won't copy the already read! */
3623 else if (bufremain < 10) {
3624 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3626 FB->ReadWritePointer = FB->Buf->buf;
3628 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3633 FB->ReadWritePointer = FB->Buf->buf;
3634 bufremain = FB->Buf->BufSize - 1;
3637 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3640 FB->Buf->BufUsed += n;
3641 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3646 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3651 if ((FB == NULL) || (FB->Buf == NULL))
3654 if (FB->ReadWritePointer != NULL)
3656 WriteRemain = FB->Buf->BufUsed -
3657 (FB->ReadWritePointer -
3661 FB->ReadWritePointer = FB->Buf->buf;
3662 WriteRemain = FB->Buf->BufUsed;
3665 n = write(fd, FB->ReadWritePointer, WriteRemain);
3667 FB->ReadWritePointer += n;
3669 if (FB->ReadWritePointer ==
3670 FB->Buf->buf + FB->Buf->BufUsed)
3672 FlushStrBuf(FB->Buf);
3673 FB->ReadWritePointer = NULL;
3676 // check whether we've got something to write
3677 // get the maximum chunk plus the pointer we can send
3678 // write whats there
3679 // if not all was sent, remember the send pointer for the next time
3680 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3686 * @ingroup StrBuf_IO
3687 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3688 * @param LineBuf your line will be copied here.
3689 * @param FB BLOB with lines of text...
3690 * @param Ptr moved arround to keep the next-line across several iterations
3691 * has to be &NULL on start; will be &NotNULL on end of buffer
3692 * @returns size of copied buffer
3694 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3696 const char *aptr, *ptr, *eptr;
3699 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3700 FB->ReadWritePointer = StrBufNOTNULL;
3704 FlushStrBuf(LineBuf);
3705 if (FB->ReadWritePointer == NULL)
3706 ptr = aptr = FB->Buf->buf;
3708 ptr = aptr = FB->ReadWritePointer;
3710 optr = LineBuf->buf;
3711 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3712 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3714 while ((ptr <= eptr) &&
3721 LineBuf->BufUsed = optr - LineBuf->buf;
3722 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3723 optr = LineBuf->buf + LineBuf->BufUsed;
3724 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3729 if (optr > LineBuf->buf)
3731 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3732 LineBuf->BufUsed = optr - LineBuf->buf;
3734 if ((FB->ReadWritePointer != NULL) &&
3735 (FB->ReadWritePointer != FB->Buf->buf))
3737 /* Ok, the client application read all the data
3738 it was interested in so far. Since there is more to read,
3739 we now shrink the buffer, and move the rest over.
3741 StrBufCutLeft(FB->Buf,
3742 FB->ReadWritePointer - FB->Buf->buf);
3743 FB->ReadWritePointer = FB->Buf->buf;
3745 return eMustReadMore;
3748 LineBuf->BufUsed = optr - LineBuf->buf;
3750 if ((ptr <= eptr) && (*ptr == '\r'))
3752 if ((ptr <= eptr) && (*ptr == '\n'))
3756 FB->ReadWritePointer = ptr;
3759 FlushStrBuf(FB->Buf);
3760 FB->ReadWritePointer = NULL;
3763 return eReadSuccess;
3767 * @ingroup StrBuf_CHUNKED_IO
3768 * @brief check whether the chunk-buffer has more data waiting or not.
3769 * @param FB Chunk-Buffer to inspect
3771 eReadState StrBufCheckBuffer(IOBuffer *FB)
3775 if (FB->Buf->BufUsed == 0)
3776 return eReadSuccess;
3777 if (FB->ReadWritePointer == NULL)
3778 return eBufferNotEmpty;
3779 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3780 return eBufferNotEmpty;
3781 return eReadSuccess;
3784 long IOBufferStrLength(IOBuffer *FB)
3786 if (FB->ReadWritePointer == NULL)
3787 return StrLength(FB->Buf);
3789 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3794 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3796 memset(FDB, 0, sizeof(FDIOBuffer));
3798 FDB->TotalSendSize = TotalSendSize;
3800 #ifndef LINUX_SENDFILE
3801 FDB->ChunkBuffer = NewStrBuf();
3806 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3809 #ifdef LINUX_SENDFILE
3811 sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3814 *Err = strerror(errno);
3817 FDB->ChunkSendRemain -= sent;
3818 return FDB->ChunkSendRemain;
3824 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3827 #ifdef LINUX_SENDFILE
3829 sent = sendfile(FDB->OtherFD, FDB->IOB->fd, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3832 *Err = strerror(errno);
3835 FDB->ChunkSendRemain -= sent;
3836 return FDB->ChunkSendRemain;
3842 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
3848 int nSuccessLess = 0;
3852 fdflags = fcntl(FDB->OtherFD, F_GETFL);
3853 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3855 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
3856 (FDB->ChunkSendRemain > 0))
3859 tv.tv_sec = 1; /* selectresolution; */
3863 FD_SET(FDB->OtherFD, &rfds);
3864 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
3865 *Error = strerror(errno);
3869 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
3874 should_write = FDB->IOB->Buf->BufUsed -
3875 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
3876 if (should_write > FDB->ChunkSendRemain)
3877 should_write = FDB->ChunkSendRemain;
3879 rlen = write(FDB->OtherFD,
3880 FDB->IOB->ReadWritePointer,
3883 *Error = strerror(errno);
3887 FDB->TotalSentAlready += rlen;
3888 FDB->IOB->ReadWritePointer += rlen;
3889 FDB->ChunkSendRemain -= rlen;
3891 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
3893 FlushStrBuf(FDB->IOB->Buf);
3894 FDB->IOB->ReadWritePointer = NULL;
3897 if (FDB->ChunkSendRemain == 0)
3898 return eReadSuccess;
3900 return eMustReadMore;
3903 /*******************************************************************************
3904 * File I/O; Prefer buffered read since its faster! *
3905 *******************************************************************************/
3908 * @ingroup StrBuf_IO
3909 * @brief Read a line from socket
3910 * flushes and closes the FD on error
3911 * @param buf the buffer to get the input to
3912 * @param fd pointer to the filedescriptor to read
3913 * @param append Append to an existing string or replace?
3914 * @param Error strerror() on error
3915 * @returns numbers of chars read
3917 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
3919 int len, rlen, slen;
3924 slen = len = buf->BufUsed;
3926 rlen = read(*fd, &buf->buf[len], 1);
3928 *Error = strerror(errno);
3935 if (buf->buf[len] == '\n')
3937 if (buf->buf[len] != '\r')
3939 if (len + 2 >= buf->BufSize) {
3941 buf->buf[len+1] = '\0';
3942 IncreaseBuf(buf, 1, -1);
3946 buf->buf[len] = '\0';
3951 * @ingroup StrBuf_BufferedIO
3952 * @brief Read a line from socket
3953 * flushes and closes the FD on error
3954 * @param Line the line to read from the fd / I/O Buffer
3955 * @param buf the buffer to get the input to
3956 * @param fd pointer to the filedescriptor to read
3957 * @param timeout number of successless selects until we bail out
3958 * @param selectresolution how long to wait on each select
3959 * @param Error strerror() on error
3960 * @returns numbers of chars read
3962 int StrBufTCP_read_buffered_line(StrBuf *Line,
3966 int selectresolution,
3970 int nSuccessLess = 0;
3977 if (buf->BufUsed > 0) {
3978 pch = strchr(buf->buf, '\n');
3981 len = pch - buf->buf;
3982 if (len > 0 && (*(pch - 1) == '\r') )
3984 StrBufSub(Line, buf, 0, len - rlen);
3985 StrBufCutLeft(buf, len + 1);
3990 if (buf->BufSize - buf->BufUsed < 10)
3991 IncreaseBuf(buf, 1, -1);
3993 fdflags = fcntl(*fd, F_GETFL);
3994 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3996 while ((nSuccessLess < timeout) && (pch == NULL)) {
3998 tv.tv_sec = selectresolution;
4003 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4004 *Error = strerror(errno);
4010 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4015 &buf->buf[buf->BufUsed],
4016 buf->BufSize - buf->BufUsed - 1);
4018 *Error = strerror(errno);
4023 else if (rlen > 0) {
4025 buf->BufUsed += rlen;
4026 buf->buf[buf->BufUsed] = '\0';
4027 if (buf->BufUsed + 10 > buf->BufSize) {
4028 IncreaseBuf(buf, 1, -1);
4030 pch = strchr(buf->buf, '\n');
4037 len = pch - buf->buf;
4038 if (len > 0 && (*(pch - 1) == '\r') )
4040 StrBufSub(Line, buf, 0, len - rlen);
4041 StrBufCutLeft(buf, len + 1);
4048 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4049 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4050 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4052 * @ingroup StrBuf_BufferedIO
4053 * @brief Read a line from socket
4054 * flushes and closes the FD on error
4055 * @param Line where to append our Line read from the fd / I/O Buffer;
4056 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4057 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4058 * @param fd pointer to the filedescriptor to read
4059 * @param timeout number of successless selects until we bail out
4060 * @param selectresolution how long to wait on each select
4061 * @param Error strerror() on error
4062 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4064 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4069 int selectresolution,
4072 const char *pche = NULL;
4073 const char *pos = NULL;
4075 int len, rlen, retlen;
4076 int nSuccessLess = 0;
4078 const char *pch = NULL;
4084 if ((Line == NULL) ||
4091 *Error = ErrRBLF_PreConditionFailed;
4096 if ((IOBuf->BufUsed > 0) &&
4098 (pos < IOBuf->buf + IOBuf->BufUsed))
4102 pche = IOBuf->buf + IOBuf->BufUsed;
4106 while ((pch < pche) && (*pch != '\n'))
4108 if (Line->BufUsed + 10 > Line->BufSize)
4111 apos = pcht - Line->buf;
4113 IncreaseBuf(Line, 1, -1);
4114 pcht = Line->buf + apos;
4122 if (len > 0 && (*(pch - 1) == '\r') )
4131 if ((pch >= pche) || (*pch == '\0'))
4139 if ((pch != NULL) &&
4142 if (pch + 1 >= pche) {
4155 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4157 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4158 IncreaseBuf(IOBuf, 1, -1);
4160 fdflags = fcntl(*fd, F_GETFL);
4161 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4164 while ((nSuccessLess < timeout) &&
4174 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4175 *Error = strerror(errno);
4179 *Error = ErrRBLF_SelectFailed;
4182 if (! FD_ISSET(*fd, &rfds) != 0) {
4188 &IOBuf->buf[IOBuf->BufUsed],
4189 IOBuf->BufSize - IOBuf->BufUsed - 1);
4191 *Error = strerror(errno);
4196 else if (rlen > 0) {
4198 pLF = IOBuf->buf + IOBuf->BufUsed;
4199 IOBuf->BufUsed += rlen;
4200 IOBuf->buf[IOBuf->BufUsed] = '\0';
4202 pche = IOBuf->buf + IOBuf->BufUsed;
4204 while ((pLF < pche) && (*pLF != '\n'))
4206 if ((pLF >= pche) || (*pLF == '\0'))
4209 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4213 if (pLF != NULL) apos = pLF - IOBuf->buf;
4214 IncreaseBuf(IOBuf, 1, -1);
4215 if (pLF != NULL) pLF = IOBuf->buf + apos;
4225 if (len > 0 && (*(pLF - 1) == '\r') )
4227 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4228 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4234 return retlen + len;
4236 *Error = ErrRBLF_NotEnoughSentFromServer;
4241 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4243 * @ingroup StrBuf_IO
4244 * @brief Input binary data from socket
4245 * flushes and closes the FD on error
4246 * @param Buf the buffer to get the input to
4247 * @param fd pointer to the filedescriptor to read
4248 * @param append Append to an existing string or replace?
4249 * @param nBytes the maximal number of bytes to read
4250 * @param Error strerror() on error
4251 * @returns numbers of chars read
4253 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4264 if ((Buf == NULL) || (*fd == -1))
4266 *Error = ErrRBLF_BLOBPreConditionFailed;
4271 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4272 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4274 ptr = Buf->buf + Buf->BufUsed;
4276 fdflags = fcntl(*fd, F_GETFL);
4277 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4279 while ((nRead < nBytes) &&
4289 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4290 *Error = strerror(errno);
4294 *Error = ErrRBLF_SelectFailed;
4297 if (! FD_ISSET(*fd, &rfds) != 0) {
4303 if ((rlen = read(*fd,
4305 nBytes - nRead)) == -1) {
4308 *Error = strerror(errno);
4313 Buf->BufUsed += rlen;
4315 Buf->buf[Buf->BufUsed] = '\0';
4319 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4320 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4322 * @ingroup StrBuf_BufferedIO
4323 * @brief Input binary data from socket
4324 * flushes and closes the FD on error
4325 * @param Blob put binary thing here
4326 * @param IOBuf the buffer to get the input to
4327 * @param Pos offset inside of IOBuf
4328 * @param fd pointer to the filedescriptor to read
4329 * @param append Append to an existing string or replace?
4330 * @param nBytes the maximal number of bytes to read
4331 * @param check whether we should search for '000\n' terminators in case of timeouts
4332 * @param Error strerror() on error
4333 * @returns numbers of chars read
4335 int StrBufReadBLOBBuffered(StrBuf *Blob,
4349 int nAlreadyRead = 0;
4354 int nSuccessLess = 0;
4357 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4361 *Error = ErrRBB_BLOBFPreConditionFailed;
4367 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4368 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4373 len = pos - IOBuf->buf;
4374 rlen = IOBuf->BufUsed - len;
4377 if ((IOBuf->BufUsed > 0) &&
4379 (pos < IOBuf->buf + IOBuf->BufUsed))
4381 if (rlen < nBytes) {
4382 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4383 Blob->BufUsed += rlen;
4384 Blob->buf[Blob->BufUsed] = '\0';
4385 nAlreadyRead = nRead = rlen;
4388 if (rlen >= nBytes) {
4389 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4390 Blob->BufUsed += nBytes;
4391 Blob->buf[Blob->BufUsed] = '\0';
4392 if (rlen == nBytes) {
4404 if (IOBuf->BufSize < nBytes - nRead)
4405 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4408 len = Blob->BufUsed;
4410 fdflags = fcntl(*fd, F_GETFL);
4411 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4419 while ((nSuccessLess < MaxTries) &&
4429 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4430 *Error = strerror(errno);
4434 *Error = ErrRBLF_SelectFailed;
4437 if (! FD_ISSET(*fd, &rfds) != 0) {
4444 IOBuf->BufSize - (ptr - IOBuf->buf));
4448 *Error = strerror(errno);
4451 else if (rlen == 0){
4452 if ((check == NNN_TERM) &&
4454 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4456 StrBufPlain(Blob, HKEY("\n000\n"));
4457 StrBufCutRight(Blob, 5);
4458 return Blob->BufUsed;
4460 else if (!IsNonBlock)
4462 else if (nSuccessLess > MaxTries) {
4464 *Error = ErrRBB_too_many_selects;
4468 else if (rlen > 0) {
4472 IOBuf->BufUsed += rlen;
4475 if (nSuccessLess >= MaxTries) {
4477 *Error = ErrRBB_too_many_selects;
4481 if (nRead > nBytes) {
4482 *Pos = IOBuf->buf + nBytes;
4484 Blob->buf[Blob->BufUsed] = '\0';
4485 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4489 return nRead + nAlreadyRead;
4493 * @ingroup StrBuf_IO
4494 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4495 * @param LineBuf your line will be copied here.
4496 * @param Buf BLOB with lines of text...
4497 * @param Ptr moved arround to keep the next-line across several iterations
4498 * has to be &NULL on start; will be &NotNULL on end of buffer
4499 * @returns size of remaining buffer
4501 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4503 const char *aptr, *ptr, *eptr;
4506 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4507 *Ptr = StrBufNOTNULL;
4511 FlushStrBuf(LineBuf);
4513 ptr = aptr = Buf->buf;
4517 optr = LineBuf->buf;
4518 eptr = Buf->buf + Buf->BufUsed;
4519 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4521 while ((ptr <= eptr) &&
4528 LineBuf->BufUsed = optr - LineBuf->buf;
4529 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4530 optr = LineBuf->buf + LineBuf->BufUsed;
4531 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4535 if ((ptr >= eptr) && (optr > LineBuf->buf))
4537 LineBuf->BufUsed = optr - LineBuf->buf;
4539 if ((ptr <= eptr) && (*ptr == '\r'))
4541 if ((ptr <= eptr) && (*ptr == '\n'))
4548 *Ptr = StrBufNOTNULL;
4551 return Buf->BufUsed - (ptr - Buf->buf);
4556 * @ingroup StrBuf_IO
4557 * @brief removes double slashes from pathnames
4558 * @param Dir directory string to filter
4559 * @param RemoveTrailingSlash allows / disallows trailing slashes
4561 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4567 while (!IsEmptyStr(a)) {
4579 if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
4584 Dir->BufUsed = b - Dir->buf;