2 * Copyright (c) 1987-2011 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/select.h>
29 #include <sys/types.h>
30 #define SHOW_ME_VAPPEND_PRINTF
33 #ifndef LINUX_SENDFILE
34 #include <sys/sendfile.h>
36 #include "libcitadel.h"
47 #include <sys/sendfile.h>
52 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
53 const Bytef * source, uLong sourceLen, int level);
55 int BaseStrBufSize = 64;
57 const char *StrBufNOTNULL = ((char*) NULL) - 1;
59 const char HexList[256][3] = {
60 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
61 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
62 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
63 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
64 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
65 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
66 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
67 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
68 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
69 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
70 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
71 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
72 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
73 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
74 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
75 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
78 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
79 * StrBuf is a versatile class, aiding the handling of dynamic strings
80 * * reduce de/reallocations
81 * * reduce the need to remeasure it
82 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
83 * * allow asyncroneous IO for line and Blob based operations
84 * * reduce the use of memove in those
85 * * Quick filling in several operations with append functions
89 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
94 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
96 * use these operators to interfere with code demanding char*;
97 * if you need to own the content, smash me. Avoid, since we loose the length information.
101 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
103 * operations to get your Strings into a StrBuf, manipulating them, or appending
106 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
108 * Quick tokenizer; demands of the user to pull its tokens in sequence
112 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
114 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
118 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
120 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
121 * External Cursor to maintain the current read position inside of the buffer
122 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
126 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
132 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
134 * these functions translate the content of a buffer into another representation;
135 * some are combined Fillers and encoders
139 * Private Structure for the Stringbuffer
142 char *buf; /**< the pointer to the dynamic buffer */
143 long BufSize; /**< how many spcae do we optain */
144 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
145 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
147 long nIncreases; /**< for profiling; cound how many times we needed more */
148 char bt [SIZ]; /**< Stacktrace of last increase */
149 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
154 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
155 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
158 #ifdef HAVE_BACKTRACE
159 static void StrBufBacktrace(StrBuf *Buf, int which)
163 void *stack_frames[50];
168 pstart = pch = Buf->bt;
170 pstart = pch = Buf->bt_lastinc;
171 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
172 strings = backtrace_symbols(stack_frames, size);
173 for (i = 0; i < size; i++) {
175 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
177 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
186 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
188 if (hFreeDbglog == -1){
189 pid_t pid = getpid();
191 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
192 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
194 if ((*FreeMe)->nIncreases > 0)
198 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
200 (*FreeMe)->nIncreases,
204 (*FreeMe)->bt_lastinc);
205 n = write(hFreeDbglog, buf, n);
211 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
215 n = write(hFreeDbglog, buf, n);
219 void dbg_IncreaseBuf(StrBuf *IncMe)
222 #ifdef HAVE_BACKTRACE
223 StrBufBacktrace(Buf, 1);
227 void dbg_Init(StrBuf *Buf)
231 Buf->bt_lastinc[0] = '\0';
232 #ifdef HAVE_BACKTRACE
233 StrBufBacktrace(Buf, 0);
239 #define dbg_FreeStrBuf(a, b)
240 #define dbg_IncreaseBuf(a)
247 * @brief swaps the contents of two StrBufs
248 * this is to be used to have cheap switched between a work-buffer and a target buffer
250 * @param B second one
252 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
256 memcpy(&C, A, sizeof(*A));
257 memcpy(A, B, sizeof(*B));
258 memcpy(B, &C, sizeof(C));
263 * @ingroup StrBuf_Cast
264 * @brief Cast operator to Plain String
265 * @note if the buffer is altered by StrBuf operations, this pointer may become
266 * invalid. So don't lean on it after altering the buffer!
267 * Since this operation is considered cheap, rather call it often than risking
268 * your pointer to become invalid!
269 * @param Str the string we want to get the c-string representation for
270 * @returns the Pointer to the Content. Don't mess with it!
272 inline const char *ChrPtr(const StrBuf *Str)
280 * @ingroup StrBuf_Cast
281 * @brief since we know strlen()'s result, provide it here.
282 * @param Str the string to return the length to
283 * @returns contentlength of the buffer
285 inline int StrLength(const StrBuf *Str)
287 return (Str != NULL) ? Str->BufUsed : 0;
291 * @ingroup StrBuf_DeConstructors
292 * @brief local utility function to resize the buffer
293 * @param Buf the buffer whichs storage we should increase
294 * @param KeepOriginal should we copy the original buffer or just start over with a new one
295 * @param DestSize what should fit in after?
297 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
300 size_t NewSize = Buf->BufSize * 2;
306 while ((NewSize <= DestSize) && (NewSize != 0))
312 NewBuf= (char*) malloc(NewSize);
316 if (KeepOriginal && (Buf->BufUsed > 0))
318 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
327 Buf->BufSize = NewSize;
329 dbg_IncreaseBuf(Buf);
335 * @ingroup StrBuf_DeConstructors
336 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
337 * @param Buf Buffer to shrink (has to be empty)
338 * @param ThreshHold if the buffer is bigger then this, its readjusted
339 * @param NewSize if we Shrink it, how big are we going to be afterwards?
341 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
344 (Buf->BufUsed == 0) &&
345 (Buf->BufSize < ThreshHold)) {
347 Buf->buf = (char*) malloc(NewSize);
349 Buf->BufSize = NewSize;
354 * @ingroup StrBuf_DeConstructors
355 * @brief shrink long term buffers to their real size so they don't waste memory
356 * @param Buf buffer to shrink
357 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
358 * @returns physical size of the buffer
360 long StrBufShrinkToFit(StrBuf *Buf, int Force)
365 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
367 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
368 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
369 Buf->BufSize = Buf->BufUsed + 1;
377 * @ingroup StrBuf_DeConstructors
378 * @brief Allocate a new buffer with default buffer size
379 * @returns the new stringbuffer
381 StrBuf* NewStrBuf(void)
385 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
386 NewBuf->buf = (char*) malloc(BaseStrBufSize);
387 NewBuf->buf[0] = '\0';
388 NewBuf->BufSize = BaseStrBufSize;
390 NewBuf->ConstBuf = 0;
398 * @ingroup StrBuf_DeConstructors
399 * @brief Copy Constructor; returns a duplicate of CopyMe
400 * @param CopyMe Buffer to faxmilate
401 * @returns the new stringbuffer
403 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
410 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
411 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
412 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
413 NewBuf->BufUsed = CopyMe->BufUsed;
414 NewBuf->BufSize = CopyMe->BufSize;
415 NewBuf->ConstBuf = 0;
423 * @ingroup StrBuf_DeConstructors
424 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
425 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
426 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
427 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
428 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
429 * @returns the new stringbuffer
431 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
435 if (CreateRelpaceMe == NULL)
440 if (*CreateRelpaceMe != NULL)
441 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
443 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
447 if (CopyFlushMe == NULL)
449 if (*CreateRelpaceMe != NULL)
450 FlushStrBuf(*CreateRelpaceMe);
452 *CreateRelpaceMe = NewStrBuf();
457 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
458 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
459 * be a big IO-Buffer...
461 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
463 if (*CreateRelpaceMe == NULL)
465 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
470 NewBuf = *CreateRelpaceMe;
473 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
477 if (*CreateRelpaceMe == NULL)
479 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
483 NewBuf = *CreateRelpaceMe;
484 SwapBuffers (NewBuf, CopyFlushMe);
487 FlushStrBuf(CopyFlushMe);
492 * @ingroup StrBuf_DeConstructors
493 * @brief create a new Buffer using an existing c-string
494 * this function should also be used if you want to pre-suggest
495 * the buffer size to allocate in conjunction with ptr == NULL
496 * @param ptr the c-string to copy; may be NULL to create a blank instance
497 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
498 * @returns the new stringbuffer
500 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
503 size_t Siz = BaseStrBufSize;
506 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
508 CopySize = strlen((ptr != NULL)?ptr:"");
512 while ((Siz <= CopySize) && (Siz != 0))
520 NewBuf->buf = (char*) malloc(Siz);
521 if (NewBuf->buf == NULL)
526 NewBuf->BufSize = Siz;
528 memcpy(NewBuf->buf, ptr, CopySize);
529 NewBuf->buf[CopySize] = '\0';
530 NewBuf->BufUsed = CopySize;
533 NewBuf->buf[0] = '\0';
536 NewBuf->ConstBuf = 0;
544 * @ingroup StrBuf_DeConstructors
545 * @brief Set an existing buffer from a c-string
546 * @param Buf buffer to load
547 * @param ptr c-string to put into
548 * @param nChars set to -1 if we should work 0-terminated
549 * @returns the new length of the string
551 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
566 CopySize = strlen(ptr);
570 while ((Siz <= CopySize) && (Siz != 0))
578 if (Siz != Buf->BufSize)
579 IncreaseBuf(Buf, 0, Siz);
580 memcpy(Buf->buf, ptr, CopySize);
581 Buf->buf[CopySize] = '\0';
582 Buf->BufUsed = CopySize;
589 * @ingroup StrBuf_DeConstructors
590 * @brief use strbuf as wrapper for a string constant for easy handling
591 * @param StringConstant a string to wrap
592 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
594 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
598 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
599 NewBuf->buf = (char*) StringConstant;
600 NewBuf->BufSize = SizeOfStrConstant;
601 NewBuf->BufUsed = SizeOfStrConstant;
602 NewBuf->ConstBuf = 1;
611 * @ingroup StrBuf_DeConstructors
612 * @brief flush the content of a Buf; keep its struct
613 * @param buf Buffer to flush
615 int FlushStrBuf(StrBuf *buf)
627 * @ingroup StrBuf_DeConstructors
628 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
629 * @param buf Buffer to wipe
631 int FLUSHStrBuf(StrBuf *buf)
637 if (buf->BufUsed > 0) {
638 memset(buf->buf, 0, buf->BufUsed);
645 int hFreeDbglog = -1;
648 * @ingroup StrBuf_DeConstructors
649 * @brief Release a Buffer
650 * Its a double pointer, so it can NULL your pointer
651 * so fancy SIG11 appear instead of random results
652 * @param FreeMe Pointer Pointer to the buffer to free
654 void FreeStrBuf (StrBuf **FreeMe)
659 dbg_FreeStrBuf(FreeMe, 'F');
661 if (!(*FreeMe)->ConstBuf)
662 free((*FreeMe)->buf);
668 * @ingroup StrBuf_DeConstructors
669 * @brief flatten a Buffer to the Char * we return
670 * Its a double pointer, so it can NULL your pointer
671 * so fancy SIG11 appear instead of random results
672 * The Callee then owns the buffer and is responsible for freeing it.
673 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
674 * @returns the pointer of the buffer; Callee owns the memory thereafter.
676 char *SmashStrBuf (StrBuf **SmashMe)
680 if ((SmashMe == NULL) || (*SmashMe == NULL))
683 dbg_FreeStrBuf(SmashMe, 'S');
685 Ret = (*SmashMe)->buf;
692 * @ingroup StrBuf_DeConstructors
693 * @brief Release the buffer
694 * If you want put your StrBuf into a Hash, use this as Destructor.
695 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
697 void HFreeStrBuf (void *VFreeMe)
699 StrBuf *FreeMe = (StrBuf*)VFreeMe;
703 dbg_FreeStrBuf(SmashMe, 'H');
705 if (!FreeMe->ConstBuf)
711 /*******************************************************************************
712 * Simple string transformations *
713 *******************************************************************************/
717 * @brief Wrapper around atol
719 long StrTol(const StrBuf *Buf)
724 return atol(Buf->buf);
731 * @brief Wrapper around atoi
733 int StrToi(const StrBuf *Buf)
737 if (Buf->BufUsed > 0)
738 return atoi(Buf->buf);
745 * @brief Checks to see if the string is a pure number
746 * @param Buf The buffer to inspect
747 * @returns 1 if its a pure number, 0, if not.
749 int StrBufIsNumber(const StrBuf *Buf) {
751 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
754 strtoll(Buf->buf, &pEnd, 10);
755 if (pEnd == Buf->buf)
757 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
759 if (Buf->buf == pEnd)
765 * @ingroup StrBuf_Filler
766 * @brief modifies a Single char of the Buf
767 * You can point to it via char* or a zero-based integer
768 * @param Buf The buffer to manipulate
769 * @param ptr char* to zero; use NULL if unused
770 * @param nThChar zero based pointer into the string; use -1 if unused
771 * @param PeekValue The Character to place into the position
773 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
778 nThChar = ptr - Buf->buf;
779 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
781 Buf->buf[nThChar] = PeekValue;
786 * @ingroup StrBuf_Filler
787 * @brief modifies a range of chars of the Buf
788 * You can point to it via char* or a zero-based integer
789 * @param Buf The buffer to manipulate
790 * @param ptr char* to zero; use NULL if unused
791 * @param nThChar zero based pointer into the string; use -1 if unused
792 * @param nChars how many chars are to be flushed?
793 * @param PookValue The Character to place into that area
795 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
800 nThChar = ptr - Buf->buf;
801 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
803 if (nThChar + nChars > Buf->BufUsed)
804 nChars = Buf->BufUsed - nThChar;
806 memset(Buf->buf + nThChar, PookValue, nChars);
807 /* just to be shure... */
808 Buf->buf[Buf->BufUsed] = 0;
813 * @ingroup StrBuf_Filler
814 * @brief Append a StringBuffer to the buffer
815 * @param Buf Buffer to modify
816 * @param AppendBuf Buffer to copy at the end of our buffer
817 * @param Offset Should we start copying from an offset?
819 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
821 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
824 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
827 AppendBuf->BufUsed + Buf->BufUsed);
829 memcpy(Buf->buf + Buf->BufUsed,
830 AppendBuf->buf + Offset,
831 AppendBuf->BufUsed - Offset);
832 Buf->BufUsed += AppendBuf->BufUsed - Offset;
833 Buf->buf[Buf->BufUsed] = '\0';
838 * @ingroup StrBuf_Filler
839 * @brief Append a C-String to the buffer
840 * @param Buf Buffer to modify
841 * @param AppendBuf Buffer to copy at the end of our buffer
842 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
843 * @param Offset Should we start copying from an offset?
845 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
848 long BufSizeRequired;
850 if ((AppendBuf == NULL) || (Buf == NULL))
854 aps = strlen(AppendBuf + Offset);
856 aps = AppendSize - Offset;
858 BufSizeRequired = Buf->BufUsed + aps + 1;
859 if (Buf->BufSize <= BufSizeRequired)
860 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
862 memcpy(Buf->buf + Buf->BufUsed,
866 Buf->buf[Buf->BufUsed] = '\0';
870 * @ingroup StrBuf_Filler
871 * @brief sprintf like function appending the formated string to the buffer
872 * vsnprintf version to wrap into own calls
873 * @param Buf Buffer to extend by format and Params
874 * @param format printf alike format to add
875 * @param ap va_list containing the items for format
877 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
885 if ((Buf == NULL) || (format == NULL))
888 BufSize = Buf->BufSize;
889 nWritten = Buf->BufSize + 1;
890 Offset = Buf->BufUsed;
891 newused = Offset + nWritten;
893 while (newused >= BufSize) {
895 nWritten = vsnprintf(Buf->buf + Offset,
896 Buf->BufSize - Offset,
899 newused = Offset + nWritten;
900 if (newused >= Buf->BufSize) {
901 if (IncreaseBuf(Buf, 1, newused) == -1)
902 return; /* TODO: error handling? */
903 newused = Buf->BufSize + 1;
906 Buf->BufUsed = Offset + nWritten;
907 BufSize = Buf->BufSize;
914 * @ingroup StrBuf_Filler
915 * @brief sprintf like function appending the formated string to the buffer
916 * @param Buf Buffer to extend by format and Params
917 * @param format printf alike format to add
919 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
927 if ((Buf == NULL) || (format == NULL))
930 BufSize = Buf->BufSize;
931 nWritten = Buf->BufSize + 1;
932 Offset = Buf->BufUsed;
933 newused = Offset + nWritten;
935 while (newused >= BufSize) {
936 va_start(arg_ptr, format);
937 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
938 Buf->BufSize - Buf->BufUsed,
941 newused = Buf->BufUsed + nWritten;
942 if (newused >= Buf->BufSize) {
943 if (IncreaseBuf(Buf, 1, newused) == -1)
944 return; /* TODO: error handling? */
945 newused = Buf->BufSize + 1;
948 Buf->BufUsed += nWritten;
949 BufSize = Buf->BufSize;
956 * @ingroup StrBuf_Filler
957 * @brief sprintf like function putting the formated string into the buffer
958 * @param Buf Buffer to extend by format and Parameters
959 * @param format printf alike format to add
961 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
966 if ((Buf == NULL) || (format == NULL))
969 nWritten = Buf->BufSize + 1;
970 while (nWritten >= Buf->BufSize) {
971 va_start(arg_ptr, format);
972 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
974 if (nWritten >= Buf->BufSize) {
975 if (IncreaseBuf(Buf, 0, 0) == -1)
976 return; /* TODO: error handling? */
977 nWritten = Buf->BufSize + 1;
980 Buf->BufUsed = nWritten ;
985 * @ingroup StrBuf_Filler
986 * @brief Callback for cURL to append the webserver reply to a buffer
987 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
988 * @param size pre-defined by the cURL API; see man 3 curl for mre info
989 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
990 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
992 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1001 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1002 return size * nmemb;
1008 * @brief extracts a substring from Source into dest
1009 * @param dest buffer to place substring into
1010 * @param Source string to copy substring from
1011 * @param Offset chars to skip from start
1012 * @param nChars number of chars to copy
1013 * @returns the number of chars copied; may be different from nChars due to the size of Source
1015 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1017 size_t NCharsRemain;
1018 if (Offset > Source->BufUsed)
1024 if (Offset + nChars < Source->BufUsed)
1026 if (nChars >= dest->BufSize)
1027 IncreaseBuf(dest, 0, nChars + 1);
1028 memcpy(dest->buf, Source->buf + Offset, nChars);
1029 dest->BufUsed = nChars;
1030 dest->buf[dest->BufUsed] = '\0';
1033 NCharsRemain = Source->BufUsed - Offset;
1034 if (NCharsRemain >= dest->BufSize)
1035 IncreaseBuf(dest, 0, NCharsRemain + 1);
1036 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1037 dest->BufUsed = NCharsRemain;
1038 dest->buf[dest->BufUsed] = '\0';
1039 return NCharsRemain;
1044 * @brief Cut nChars from the start of the string
1045 * @param Buf Buffer to modify
1046 * @param nChars how many chars should be skipped?
1048 void StrBufCutLeft(StrBuf *Buf, int nChars)
1050 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1051 if (nChars >= Buf->BufUsed) {
1055 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1056 Buf->BufUsed -= nChars;
1057 Buf->buf[Buf->BufUsed] = '\0';
1062 * @brief Cut the trailing n Chars from the string
1063 * @param Buf Buffer to modify
1064 * @param nChars how many chars should be trunkated?
1066 void StrBufCutRight(StrBuf *Buf, int nChars)
1068 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1069 if (nChars >= Buf->BufUsed) {
1073 Buf->BufUsed -= nChars;
1074 Buf->buf[Buf->BufUsed] = '\0';
1079 * @brief Cut the string after n Chars
1080 * @param Buf Buffer to modify
1081 * @param AfternChars after how many chars should we trunkate the string?
1082 * @param At if non-null and points inside of our string, cut it there.
1084 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1086 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1088 AfternChars = At - Buf->buf;
1091 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1093 Buf->BufUsed = AfternChars;
1094 Buf->buf[Buf->BufUsed] = '\0';
1100 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1101 * @param Buf the string to modify
1103 void StrBufTrim(StrBuf *Buf)
1106 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1108 while ((Buf->BufUsed > 0) &&
1109 isspace(Buf->buf[Buf->BufUsed - 1]))
1113 Buf->buf[Buf->BufUsed] = '\0';
1115 if (Buf->BufUsed == 0) return;
1117 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1120 if (delta > 0) StrBufCutLeft(Buf, delta);
1124 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1125 * @param Buf the string to modify
1127 void StrBufSpaceToBlank(StrBuf *Buf)
1131 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1134 pche = pch + Buf->BufUsed;
1143 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1151 pLeft = pBuff = Buf->buf;
1152 while (pBuff != NULL) {
1154 pBuff = strchr(pBuff, leftboundary);
1163 pRight = strchr(pBuff, rightboundary);
1165 StrBufCutAt(Buf, 0, pRight);
1167 StrBufCutLeft(Buf, pLeft - Buf->buf);
1172 * @ingroup StrBuf_Filler
1173 * @brief uppercase the contents of a buffer
1174 * @param Buf the buffer to translate
1176 void StrBufUpCase(StrBuf *Buf)
1180 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1183 pche = pch + Buf->BufUsed;
1184 while (pch < pche) {
1185 *pch = toupper(*pch);
1192 * @ingroup StrBuf_Filler
1193 * @brief lowercase the contents of a buffer
1194 * @param Buf the buffer to translate
1196 void StrBufLowerCase(StrBuf *Buf)
1200 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1203 pche = pch + Buf->BufUsed;
1204 while (pch < pche) {
1205 *pch = tolower(*pch);
1211 /*******************************************************************************
1212 * a tokenizer that kills, maims, and destroys *
1213 *******************************************************************************/
1216 * @ingroup StrBuf_Tokenizer
1217 * @brief Replace a token at a given place with a given length by another token with given length
1218 * @param Buf String where to work on
1219 * @param where where inside of the Buf is the search-token
1220 * @param HowLong How long is the token to be replaced
1221 * @param Repl Token to insert at 'where'
1222 * @param ReplLen Length of repl
1223 * @returns -1 if fail else length of resulting Buf
1225 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1226 const char *Repl, long ReplLen)
1229 if ((Buf == NULL) ||
1230 (where > Buf->BufUsed) ||
1231 (where + HowLong > Buf->BufUsed))
1234 if (where + ReplLen - HowLong > Buf->BufSize)
1235 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1238 memmove(Buf->buf + where + ReplLen,
1239 Buf->buf + where + HowLong,
1240 Buf->BufUsed - where - HowLong);
1242 memcpy(Buf->buf + where,
1245 Buf->BufUsed += ReplLen - HowLong;
1247 return Buf->BufUsed;
1251 * @ingroup StrBuf_Tokenizer
1252 * @brief Counts the numbmer of tokens in a buffer
1253 * @param source String to count tokens in
1254 * @param tok Tokenizer char to count
1255 * @returns numbers of tokenizer chars found
1257 int StrBufNum_tokens(const StrBuf *source, char tok)
1261 if ((source == NULL) || (source->BufUsed == 0))
1263 if ((source->BufUsed == 1) && (*source->buf == tok))
1267 pche = pch + source->BufUsed;
1278 * @ingroup StrBuf_Tokenizer
1279 * @brief a string tokenizer
1280 * @param Source StringBuffer to read into
1281 * @param parmnum n'th Parameter to remove
1282 * @param separator tokenizer character
1283 * @returns -1 if not found, else length of token.
1285 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1288 char *d, *s, *end; /* dest, source */
1291 /* Find desired @parameter */
1292 end = Source->buf + Source->BufUsed;
1294 while ((d <= end) &&
1297 /* End of string, bail! */
1302 if (*d == separator) {
1307 if ((d == NULL) || (d >= end))
1308 return 0; /* @Parameter not found */
1310 /* Find next @parameter */
1312 while ((s <= end) &&
1313 (*s && *s != separator))
1317 if (*s == separator)
1321 /* Hack and slash */
1326 memmove(d, s, Source->BufUsed - (s - Source->buf));
1327 Source->BufUsed += ReducedBy;
1328 Source->buf[Source->BufUsed] = '\0';
1330 else if (d == Source->buf) {
1332 Source->BufUsed = 0;
1336 Source->BufUsed += ReducedBy;
1349 * @ingroup StrBuf_Tokenizer
1350 * @brief a string tokenizer
1351 * @param dest Destination StringBuffer
1352 * @param Source StringBuffer to read into
1353 * @param parmnum n'th Parameter to extract
1354 * @param separator tokenizer character
1355 * @returns -1 if not found, else length of token.
1357 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1359 const char *s, *e; //* source * /
1360 int len = 0; //* running total length of extracted string * /
1361 int current_token = 0; //* token currently being processed * /
1364 dest->buf[0] = '\0';
1370 if ((Source == NULL) || (Source->BufUsed ==0)) {
1374 e = s + Source->BufUsed;
1377 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1379 while ((s < e) && !IsEmptyStr(s)) {
1380 if (*s == separator) {
1383 if (len >= dest->BufSize) {
1384 dest->BufUsed = len;
1385 if (IncreaseBuf(dest, 1, -1) < 0) {
1390 if ( (current_token == parmnum) &&
1391 (*s != separator)) {
1392 dest->buf[len] = *s;
1395 else if (current_token > parmnum) {
1401 dest->buf[len] = '\0';
1402 dest->BufUsed = len;
1404 if (current_token < parmnum) {
1405 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1408 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1417 * @ingroup StrBuf_Tokenizer
1418 * @brief a string tokenizer to fetch an integer
1419 * @param Source String containing tokens
1420 * @param parmnum n'th Parameter to extract
1421 * @param separator tokenizer character
1422 * @returns 0 if not found, else integer representation of the token
1424 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1434 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1441 * @ingroup StrBuf_Tokenizer
1442 * @brief a string tokenizer to fetch a long integer
1443 * @param Source String containing tokens
1444 * @param parmnum n'th Parameter to extract
1445 * @param separator tokenizer character
1446 * @returns 0 if not found, else long integer representation of the token
1448 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1458 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1466 * @ingroup StrBuf_Tokenizer
1467 * @brief a string tokenizer to fetch an unsigned long
1468 * @param Source String containing tokens
1469 * @param parmnum n'th Parameter to extract
1470 * @param separator tokenizer character
1471 * @returns 0 if not found, else unsigned long representation of the token
1473 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1484 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1488 return (unsigned long) atol(pnum);
1497 * @ingroup StrBuf_NextTokenizer
1498 * @brief a string tokenizer; Bounds checker
1499 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1500 * @param Source our tokenbuffer
1501 * @param pStart the token iterator pointer to inspect
1502 * @returns whether the revolving pointer is inside of the search range
1504 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1506 if ((Source == NULL) ||
1507 (*pStart == StrBufNOTNULL) ||
1508 (Source->BufUsed == 0))
1512 if (*pStart == NULL)
1516 else if (*pStart > Source->buf + Source->BufUsed)
1520 else if (*pStart <= Source->buf)
1529 * @ingroup StrBuf_NextTokenizer
1530 * @brief a string tokenizer
1531 * @param dest Destination StringBuffer
1532 * @param Source StringBuffer to read into
1533 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1534 * @param separator tokenizer
1535 * @returns -1 if not found, else length of token.
1537 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1539 const char *s; /* source */
1540 const char *EndBuffer; /* end stop of source buffer */
1541 int current_token = 0; /* token currently being processed */
1542 int len = 0; /* running total length of extracted string */
1544 if ((Source == NULL) ||
1545 (Source->BufUsed == 0) )
1547 *pStart = StrBufNOTNULL;
1553 EndBuffer = Source->buf + Source->BufUsed;
1557 dest->buf[0] = '\0';
1562 *pStart = EndBuffer + 1;
1566 if (*pStart == NULL)
1568 *pStart = Source->buf; /* we're starting to examine this buffer. */
1570 else if ((*pStart < Source->buf) ||
1571 (*pStart > EndBuffer ) )
1573 return -1; /* no more tokens to find. */
1577 /* start to find the next token */
1578 while ((s <= EndBuffer) &&
1579 (current_token == 0) )
1581 if (*s == separator)
1583 /* we found the next token */
1587 if (len >= dest->BufSize)
1589 /* our Dest-buffer isn't big enough, increase it. */
1590 dest->BufUsed = len;
1592 if (IncreaseBuf(dest, 1, -1) < 0) {
1593 /* WHUT? no more mem? bail out. */
1600 if ( (current_token == 0 ) && /* are we in our target token? */
1601 (!IsEmptyStr(s) ) &&
1602 (separator != *s) ) /* don't copy the token itself */
1604 dest->buf[len] = *s; /* Copy the payload */
1605 ++len; /* remember the bigger size. */
1611 /* did we reach the end? */
1612 if ((s > EndBuffer)) {
1613 EndBuffer = StrBufNOTNULL;
1614 *pStart = EndBuffer;
1617 *pStart = s; /* remember the position for the next run */
1620 /* sanitize our extracted token */
1621 dest->buf[len] = '\0';
1622 dest->BufUsed = len;
1629 * @ingroup StrBuf_NextTokenizer
1630 * @brief a string tokenizer
1631 * @param Source StringBuffer to read from
1632 * @param pStart pointer to the end of the last token. Feed with NULL.
1633 * @param separator tokenizer character
1634 * @param nTokens number of tokens to fastforward over
1635 * @returns -1 if not found, else length of token.
1637 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1639 const char *s, *EndBuffer; //* source * /
1640 int len = 0; //* running total length of extracted string * /
1641 int current_token = 0; //* token currently being processed * /
1643 if ((Source == NULL) ||
1644 (Source->BufUsed ==0)) {
1648 return Source->BufUsed;
1650 if (*pStart == NULL)
1651 *pStart = Source->buf;
1653 EndBuffer = Source->buf + Source->BufUsed;
1655 if ((*pStart < Source->buf) ||
1656 (*pStart > EndBuffer)) {
1664 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1666 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1667 if (*s == separator) {
1670 if (current_token >= nTokens) {
1682 * @ingroup StrBuf_NextTokenizer
1683 * @brief a string tokenizer to fetch an integer
1684 * @param Source StringBuffer to read from
1685 * @param pStart Cursor on the tokenstring
1686 * @param separator tokenizer character
1687 * @returns 0 if not found, else integer representation of the token
1689 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1699 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1706 * @ingroup StrBuf_NextTokenizer
1707 * @brief a string tokenizer to fetch a long integer
1708 * @param Source StringBuffer to read from
1709 * @param pStart Cursor on the tokenstring
1710 * @param separator tokenizer character
1711 * @returns 0 if not found, else long integer representation of the token
1713 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1723 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1731 * @ingroup StrBuf_NextTokenizer
1732 * @brief a string tokenizer to fetch an unsigned long
1733 * @param Source StringBuffer to read from
1734 * @param pStart Cursor on the tokenstring
1735 * @param separator tokenizer character
1736 * @returns 0 if not found, else unsigned long representation of the token
1738 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1749 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1753 return (unsigned long) atol(pnum);
1763 /*******************************************************************************
1764 * Escape Appending *
1765 *******************************************************************************/
1768 * @ingroup StrBuf_DeEnCoder
1769 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1770 * @param OutBuf the output buffer
1771 * @param In Buffer to encode
1772 * @param PlainIn way in from plain old c strings
1774 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1776 const char *pch, *pche;
1780 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1782 if (PlainIn != NULL) {
1783 len = strlen(PlainIn);
1789 pche = pch + In->BufUsed;
1796 pt = OutBuf->buf + OutBuf->BufUsed;
1797 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1799 while (pch < pche) {
1801 IncreaseBuf(OutBuf, 1, -1);
1802 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1803 pt = OutBuf->buf + OutBuf->BufUsed;
1806 if((*pch >= 'a' && *pch <= 'z') ||
1807 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1808 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1809 (*pch == '!') || (*pch == '_') ||
1810 (*pch == ',') || (*pch == '.'))
1817 *(pt + 1) = HexList[(unsigned char)*pch][0];
1818 *(pt + 2) = HexList[(unsigned char)*pch][1];
1820 OutBuf->BufUsed += 3;
1828 * @ingroup StrBuf_DeEnCoder
1829 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1830 * @param OutBuf the output buffer
1831 * @param In Buffer to encode
1832 * @param PlainIn way in from plain old c strings
1834 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1836 const char *pch, *pche;
1840 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1842 if (PlainIn != NULL) {
1843 len = strlen(PlainIn);
1849 pche = pch + In->BufUsed;
1856 pt = OutBuf->buf + OutBuf->BufUsed;
1857 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1859 while (pch < pche) {
1861 IncreaseBuf(OutBuf, 1, -1);
1862 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1863 pt = OutBuf->buf + OutBuf->BufUsed;
1866 if((*pch >= 'a' && *pch <= 'z') ||
1867 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1868 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1869 (*pch == '!') || (*pch == '_') ||
1870 (*pch == ',') || (*pch == '.'))
1877 *(pt + 1) = HexList[(unsigned char)*pch][0];
1878 *(pt + 2) = HexList[(unsigned char)*pch][1];
1880 OutBuf->BufUsed += 3;
1888 * @ingroup StrBuf_DeEnCoder
1889 * @brief append a string in hex encoding to the buffer
1890 * @param OutBuf the output buffer
1891 * @param In Buffer to encode
1892 * @param PlainIn way in from plain old c strings
1893 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1895 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1897 const unsigned char *pch, *pche;
1901 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1903 if (PlainIn != NULL) {
1905 len = strlen((const char*)PlainIn);
1912 pch = (const unsigned char*)In->buf;
1913 pche = pch + In->BufUsed;
1920 pt = OutBuf->buf + OutBuf->BufUsed;
1921 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1923 while (pch < pche) {
1925 IncreaseBuf(OutBuf, 1, -1);
1926 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1927 pt = OutBuf->buf + OutBuf->BufUsed;
1930 *pt = HexList[*pch][0];
1932 *pt = HexList[*pch][1];
1933 pt ++; pch ++; OutBuf->BufUsed += 2;
1939 * @ingroup StrBuf_DeEnCoder
1940 * @brief append a string in hex encoding to the buffer
1941 * @param OutBuf the output buffer
1942 * @param In Buffer to encode
1943 * @param PlainIn way in from plain old c strings
1945 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1947 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1951 * @ingroup StrBuf_DeEnCoder
1952 * @brief Append a string, escaping characters which have meaning in HTML.
1954 * @param Target target buffer
1955 * @param Source source buffer; set to NULL if you just have a C-String
1956 * @param PlainIn Plain-C string to append; set to NULL if unused
1957 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1958 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1959 * if set to 2, linebreaks are replaced by <br/>
1961 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1963 const char *aptr, *eiptr;
1967 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1970 if (PlainIn != NULL) {
1972 len = strlen(PlainIn);
1977 eiptr = aptr + Source->BufUsed;
1978 len = Source->BufUsed;
1984 bptr = Target->buf + Target->BufUsed;
1985 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1987 while (aptr < eiptr){
1989 IncreaseBuf(Target, 1, -1);
1990 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1991 bptr = Target->buf + Target->BufUsed;
1994 memcpy(bptr, "<", 4);
1996 Target->BufUsed += 4;
1998 else if (*aptr == '>') {
1999 memcpy(bptr, ">", 4);
2001 Target->BufUsed += 4;
2003 else if (*aptr == '&') {
2004 memcpy(bptr, "&", 5);
2006 Target->BufUsed += 5;
2008 else if (*aptr == '"') {
2009 memcpy(bptr, """, 6);
2011 Target->BufUsed += 6;
2013 else if (*aptr == '\'') {
2014 memcpy(bptr, "'", 5);
2016 Target->BufUsed += 5;
2018 else if (*aptr == LB) {
2023 else if (*aptr == RB) {
2028 else if (*aptr == QU) {
2033 else if ((*aptr == 32) && (nbsp == 1)) {
2034 memcpy(bptr, " ", 6);
2036 Target->BufUsed += 6;
2038 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2039 *bptr='\0'; /* nothing */
2041 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2042 memcpy(bptr, "<br/>", 11);
2044 Target->BufUsed += 11;
2048 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2049 *bptr='\0'; /* nothing */
2059 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2061 return Target->BufUsed;
2065 * @ingroup StrBuf_DeEnCoder
2066 * @brief Append a string, escaping characters which have meaning in HTML.
2067 * Converts linebreaks into blanks; escapes single quotes
2068 * @param Target target buffer
2069 * @param Source source buffer; set to NULL if you just have a C-String
2070 * @param PlainIn Plain-C string to append; set to NULL if unused
2072 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2074 const char *aptr, *eiptr;
2078 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2081 if (PlainIn != NULL) {
2083 len = strlen(PlainIn);
2088 eiptr = aptr + Source->BufUsed;
2089 len = Source->BufUsed;
2095 eptr = Target->buf + Target->BufSize - 8;
2096 tptr = Target->buf + Target->BufUsed;
2098 while (aptr < eiptr){
2100 IncreaseBuf(Target, 1, -1);
2101 eptr = Target->buf + Target->BufSize - 8;
2102 tptr = Target->buf + Target->BufUsed;
2105 if (*aptr == '\n') {
2109 else if (*aptr == '\r') {
2113 else if (*aptr == '\'') {
2119 Target->BufUsed += 5;
2132 * @ingroup StrBuf_DeEnCoder
2133 * @brief Append a string, escaping characters which have meaning in ICAL.
2135 * @param Target target buffer
2136 * @param Source source buffer; set to NULL if you just have a C-String
2137 * @param PlainIn Plain-C string to append; set to NULL if unused
2139 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2141 const char *aptr, *eiptr;
2145 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2148 if (PlainIn != NULL) {
2150 len = strlen(PlainIn);
2155 eiptr = aptr + Source->BufUsed;
2156 len = Source->BufUsed;
2162 eptr = Target->buf + Target->BufSize - 8;
2163 tptr = Target->buf + Target->BufUsed;
2165 while (aptr < eiptr){
2166 if(tptr + 3 >= eptr) {
2167 IncreaseBuf(Target, 1, -1);
2168 eptr = Target->buf + Target->BufSize - 8;
2169 tptr = Target->buf + Target->BufUsed;
2172 if (*aptr == '\n') {
2179 else if (*aptr == '\r') {
2186 else if (*aptr == ',') {
2202 * @ingroup StrBuf_DeEnCoder
2203 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2205 * @param Target target buffer
2206 * @param Source source buffer; set to NULL if you just have a C-String
2207 * @param PlainIn Plain-C string to append; set to NULL if unused
2208 * @returns size of result or -1
2210 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2212 const char *aptr, *eiptr;
2217 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2220 if (PlainIn != NULL) {
2222 len = strlen(PlainIn);
2227 eiptr = aptr + Source->BufUsed;
2228 len = Source->BufUsed;
2234 bptr = Target->buf + Target->BufUsed;
2235 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2237 while (aptr < eiptr){
2239 IncreaseBuf(Target, 1, -1);
2240 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2241 bptr = Target->buf + Target->BufUsed;
2245 memcpy(bptr, HKEY("\\n"));
2247 Target->BufUsed += 2;
2250 memcpy(bptr, HKEY("\\r"));
2252 Target->BufUsed += 2;
2259 Target->BufUsed += 2;
2262 if ((*(aptr + 1) == 'u') &&
2263 isxdigit(*(aptr + 2)) &&
2264 isxdigit(*(aptr + 3)) &&
2265 isxdigit(*(aptr + 4)) &&
2266 isxdigit(*(aptr + 5)))
2267 { /* oh, a unicode escaper. let it pass through. */
2268 memcpy(bptr, aptr, 6);
2271 Target->BufUsed += 6;
2279 Target->BufUsed += 2;
2287 Target->BufUsed += 2;
2294 Target->BufUsed += 2;
2301 Target->BufUsed += 2;
2304 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2305 while (IsUtf8Sequence > 0){
2308 if (--IsUtf8Sequence)
2316 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2318 return Target->BufUsed;
2322 * @ingroup StrBuf_DeEnCoder
2323 * @brief Append a string, escaping characters which have meaning in HTML + json.
2325 * @param Target target buffer
2326 * @param Source source buffer; set to NULL if you just have a C-String
2327 * @param PlainIn Plain-C string to append; set to NULL if unused
2328 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2329 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2330 * if set to 2, linebreaks are replaced by <br/>
2332 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2334 const char *aptr, *eiptr;
2337 int IsUtf8Sequence = 0;
2339 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2342 if (PlainIn != NULL) {
2344 len = strlen(PlainIn);
2349 eiptr = aptr + Source->BufUsed;
2350 len = Source->BufUsed;
2356 bptr = Target->buf + Target->BufUsed;
2357 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2359 while (aptr < eiptr){
2361 IncreaseBuf(Target, 1, -1);
2362 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2363 bptr = Target->buf + Target->BufUsed;
2367 memcpy(bptr, HKEY("<"));
2369 Target->BufUsed += 4;
2372 memcpy(bptr, HKEY(">"));
2374 Target->BufUsed += 4;
2377 memcpy(bptr, HKEY("&"));
2379 Target->BufUsed += 5;
2392 switch (nolinebreaks) {
2394 *bptr='\0'; /* nothing */
2397 memcpy(bptr, HKEY("<br/>"));
2399 Target->BufUsed += 11;
2402 memcpy(bptr, HKEY("\\n"));
2404 Target->BufUsed += 2;
2408 switch (nolinebreaks) {
2411 *bptr='\0'; /* nothing */
2414 memcpy(bptr, HKEY("\\r"));
2416 Target->BufUsed += 2;
2426 Target->BufUsed += 2;
2429 if ((*(aptr + 1) == 'u') &&
2430 isxdigit(*(aptr + 2)) &&
2431 isxdigit(*(aptr + 3)) &&
2432 isxdigit(*(aptr + 4)) &&
2433 isxdigit(*(aptr + 5)))
2434 { /* oh, a unicode escaper. let it pass through. */
2435 memcpy(bptr, aptr, 6);
2438 Target->BufUsed += 6;
2446 Target->BufUsed += 2;
2454 Target->BufUsed += 2;
2461 Target->BufUsed += 2;
2468 Target->BufUsed += 2;
2472 memcpy(bptr, HKEY(" "));
2474 Target->BufUsed += 6;
2478 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2479 while (IsUtf8Sequence > 0){
2482 if (--IsUtf8Sequence)
2490 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2492 return Target->BufUsed;
2496 * @ingroup StrBuf_DeEnCoder
2497 * @brief unhide special chars hidden to the HTML escaper
2498 * @param target buffer to put the unescaped string in
2499 * @param source buffer to unescape
2501 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2507 FlushStrBuf(target);
2509 if (source == NULL ||target == NULL)
2514 len = source->BufUsed;
2515 for (a = 0; a < len; ++a) {
2516 if (target->BufUsed >= target->BufSize)
2517 IncreaseBuf(target, 1, -1);
2519 if (source->buf[a] == '=') {
2520 hex[0] = source->buf[a + 1];
2521 hex[1] = source->buf[a + 2];
2524 sscanf(hex, "%02x", &b);
2525 target->buf[target->BufUsed] = b;
2526 target->buf[++target->BufUsed] = 0;
2530 target->buf[target->BufUsed] = source->buf[a];
2531 target->buf[++target->BufUsed] = 0;
2538 * @ingroup StrBuf_DeEnCoder
2539 * @brief hide special chars from the HTML escapers and friends
2540 * @param target buffer to put the escaped string in
2541 * @param source buffer to escape
2543 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2548 FlushStrBuf(target);
2550 if (source == NULL ||target == NULL)
2555 len = source->BufUsed;
2556 for (i=0; i<len; ++i) {
2557 if (target->BufUsed + 4 >= target->BufSize)
2558 IncreaseBuf(target, 1, -1);
2559 if ( (isalnum(source->buf[i])) ||
2560 (source->buf[i]=='-') ||
2561 (source->buf[i]=='_') ) {
2562 target->buf[target->BufUsed++] = source->buf[i];
2565 sprintf(&target->buf[target->BufUsed],
2567 (0xFF &source->buf[i]));
2568 target->BufUsed += 3;
2571 target->buf[target->BufUsed + 1] = '\0';
2575 /*******************************************************************************
2576 * Quoted Printable de/encoding *
2577 *******************************************************************************/
2580 * @ingroup StrBuf_DeEnCoder
2581 * @brief decode a buffer from base 64 encoding; destroys original
2582 * @param Buf Buffor to transform
2584 int StrBufDecodeBase64(StrBuf *Buf)
2588 if (Buf == NULL) return -1;
2590 xferbuf = (char*) malloc(Buf->BufSize);
2592 siz = CtdlDecodeBase64(xferbuf,
2602 * @ingroup StrBuf_DeEnCoder
2603 * @brief decode a buffer from base 64 encoding; destroys original
2604 * @param Buf Buffor to transform
2606 int StrBufDecodeHex(StrBuf *Buf)
2609 char *pch, *pche, *pchi;
2611 if (Buf == NULL) return -1;
2613 pch = pchi = Buf->buf;
2614 pche = pch + Buf->BufUsed;
2616 while (pchi < pche){
2617 ch = decode_hex(pchi);
2624 Buf->BufUsed = pch - Buf->buf;
2625 return Buf->BufUsed;
2629 * @ingroup StrBuf_DeEnCoder
2630 * @brief replace all chars >0x20 && < 0x7F with Mute
2631 * @param Mute char to put over invalid chars
2632 * @param Buf Buffor to transform
2634 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2638 if (Buf == NULL) return -1;
2639 pch = (unsigned char *)Buf->buf;
2640 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2641 if ((*pch < 0x20) || (*pch > 0x7F))
2645 return Buf->BufUsed;
2650 * @ingroup StrBuf_DeEnCoder
2651 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2652 * @param Buf Buffer to translate
2653 * @param StripBlanks Reduce several blanks to one?
2655 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2664 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2665 Buf->buf[Buf->BufUsed - 1] = '\0';
2670 while (a < Buf->BufUsed) {
2671 if (Buf->buf[a] == '+')
2673 else if (Buf->buf[a] == '%') {
2674 /* don't let % chars through, rather truncate the input. */
2675 if (a + 2 > Buf->BufUsed) {
2680 hex[0] = Buf->buf[a + 1];
2681 hex[1] = Buf->buf[a + 2];
2684 sscanf(hex, "%02x", &b);
2685 Buf->buf[a] = (char) b;
2686 len = Buf->BufUsed - a - 2;
2688 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2700 * @ingroup StrBuf_DeEnCoder
2701 * @brief RFC2047-encode a header field if necessary.
2702 * If no non-ASCII characters are found, the string
2703 * will be copied verbatim without encoding.
2705 * @param target Target buffer.
2706 * @param source Source string to be encoded.
2707 * @returns encoded length; -1 if non success.
2709 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2711 const char headerStr[] = "=?UTF-8?Q?";
2712 int need_to_encode = 0;
2716 if ((source == NULL) ||
2720 while ((i < source->BufUsed) &&
2721 (!IsEmptyStr (&source->buf[i])) &&
2722 (need_to_encode == 0)) {
2723 if (((unsigned char) source->buf[i] < 32) ||
2724 ((unsigned char) source->buf[i] > 126)) {
2730 if (!need_to_encode) {
2731 if (*target == NULL) {
2732 *target = NewStrBufPlain(source->buf, source->BufUsed);
2735 FlushStrBuf(*target);
2736 StrBufAppendBuf(*target, source, 0);
2738 return (*target)->BufUsed;
2740 if (*target == NULL)
2741 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2742 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2743 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2744 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2745 (*target)->BufUsed = sizeof(headerStr) - 1;
2746 for (i=0; (i < source->BufUsed); ++i) {
2747 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2748 IncreaseBuf(*target, 1, 0);
2749 ch = (unsigned char) source->buf[i];
2759 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2760 (*target)->BufUsed += 3;
2764 (*target)->buf[(*target)->BufUsed] = '_';
2766 (*target)->buf[(*target)->BufUsed] = ch;
2767 (*target)->BufUsed++;
2771 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2772 IncreaseBuf(*target, 1, 0);
2774 (*target)->buf[(*target)->BufUsed++] = '?';
2775 (*target)->buf[(*target)->BufUsed++] = '=';
2776 (*target)->buf[(*target)->BufUsed] = '\0';
2777 return (*target)->BufUsed;;
2782 static void AddRecipient(StrBuf *Target,
2784 StrBuf *EmailAddress,
2789 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2790 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2792 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2793 StrBufRFC2047encode(&EncBuf, UserName);
2794 StrBufAppendBuf(Target, EncBuf, 0);
2795 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2796 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2798 if (StrLength(EmailAddress) > 0){
2799 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2800 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2801 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2807 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2808 * \param Recp Source list of email recipients
2809 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2810 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2811 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2812 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2814 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2816 StrBuf *EmailAddress,
2820 const char *pch, *pche;
2821 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2823 if ((Recp == NULL) || (StrLength(Recp) == 0))
2827 pche = pch + StrLength(Recp);
2829 if (!CheckEncode(pch, -1, pche))
2830 return NewStrBufDup(Recp);
2832 Target = NewStrBufPlain(NULL, StrLength(Recp));
2834 while ((pch != NULL) && (pch < pche))
2836 while (isspace(*pch)) pch++;
2837 UserStart = UserEnd = EmailStart = EmailEnd = NULL;
2839 if ((*pch == '"') || (*pch == '\'')) {
2840 UserStart = pch + 1;
2842 UserEnd = strchr(UserStart, *pch);
2843 if (UserEnd == NULL)
2844 break; ///TODO: Userfeedback??
2845 EmailStart = UserEnd + 1;
2846 while (isspace(*EmailStart))
2848 if (UserEnd == UserStart) {
2849 UserStart = UserEnd = NULL;
2852 if (*EmailStart == '<') {
2854 EmailEnd = strchr(EmailStart, '>');
2855 if (EmailEnd == NULL)
2856 EmailEnd = strchr(EmailStart, ',');
2860 EmailEnd = strchr(EmailStart, ',');
2862 if (EmailEnd == NULL)
2869 EmailEnd = strchr(UserStart, ',');
2870 if (EmailEnd == NULL) {
2871 EmailEnd = strchr(pch, '>');
2873 if (EmailEnd != NULL) {
2883 while ((EmailEnd > UserStart) && !gt &&
2884 ((*EmailEnd == ',') ||
2885 (*EmailEnd == '>') ||
2886 (isspace(*EmailEnd))))
2888 if (*EmailEnd == '>')
2893 if (EmailEnd == UserStart)
2897 EmailStart = strchr(UserStart, '<');
2898 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2900 UserEnd = EmailStart;
2902 while ((UserEnd > UserStart) &&
2903 isspace (*(UserEnd - 1)))
2906 if (UserStart >= UserEnd)
2907 UserStart = UserEnd = NULL;
2908 At = strchr(EmailStart, '@');
2910 else { /* this is a local recipient... no domain, just a realname */
2911 EmailStart = UserStart;
2912 At = strchr(EmailStart, '@');
2918 EmailStart = UserStart;
2924 if ((UserStart != NULL) && (UserEnd != NULL))
2925 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2926 else if ((UserStart != NULL) && (UserEnd == NULL))
2927 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2929 FlushStrBuf(UserName);
2931 if ((EmailStart != NULL) && (EmailEnd != NULL))
2932 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2933 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2934 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2936 FlushStrBuf(EmailAddress);
2938 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2943 if ((pch != NULL) && (*pch == ','))
2945 if (pch != NULL) while (isspace(*pch))
2954 * @brief replaces all occurances of 'search' by 'replace'
2955 * @param buf Buffer to modify
2956 * @param search character to search
2957 * @param replace character to replace search by
2959 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2964 for (i=0; i<buf->BufUsed; i++)
2965 if (buf->buf[i] == search)
2966 buf->buf[i] = replace;
2972 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2973 * @param buf Buffer to modify
2975 void StrBufToUnixLF(StrBuf *buf)
2977 char *pche, *pchS, *pchT;
2981 pche = buf->buf + buf->BufUsed;
2982 pchS = pchT = buf->buf;
2988 if (*pchS != '\n') {
2997 buf->BufUsed = pchT - buf->buf;
3001 /*******************************************************************************
3002 * Iconv Wrapper; RFC822 de/encoding *
3003 *******************************************************************************/
3006 * @ingroup StrBuf_DeEnCoder
3007 * @brief Wrapper around iconv_open()
3008 * Our version adds aliases for non-standard Microsoft charsets
3009 * such as 'MS950', aliasing them to names like 'CP950'
3011 * @param tocode Target encoding
3012 * @param fromcode Source encoding
3013 * @param pic anonimized pointer to iconv struct
3015 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3018 iconv_t ic = (iconv_t)(-1) ;
3019 ic = iconv_open(tocode, fromcode);
3020 if (ic == (iconv_t)(-1) ) {
3021 char alias_fromcode[64];
3022 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3023 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3024 alias_fromcode[0] = 'C';
3025 alias_fromcode[1] = 'P';
3026 ic = iconv_open(tocode, alias_fromcode);
3029 *(iconv_t *)pic = ic;
3035 * @ingroup StrBuf_DeEnCoder
3036 * @brief find one chunk of a RFC822 encoded string
3037 * @param Buffer where to search
3038 * @param bptr where to start searching
3039 * @returns found position, NULL if none.
3041 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3044 /* Find the next ?Q? */
3045 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3048 end = strchr(bptr + 2, '?');
3053 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3054 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3055 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3056 (*(end + 2) == '?')) {
3057 /* skip on to the end of the cluster, the next ?= */
3058 end = strstr(end + 3, "?=");
3061 /* sort of half valid encoding, try to find an end. */
3062 end = strstr(bptr, "?=");
3069 * @ingroup StrBuf_DeEnCoder
3070 * @brief convert one buffer according to the preselected iconv pointer PIC
3071 * @param ConvertBuf buffer we need to translate
3072 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3073 * @param pic Pointer to the iconv-session Object
3075 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3081 char *ibuf; /**< Buffer of characters to be converted */
3082 char *obuf; /**< Buffer for converted characters */
3083 size_t ibuflen; /**< Length of input buffer */
3084 size_t obuflen; /**< Length of output buffer */
3087 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3088 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3089 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3091 ic = *(iconv_t*)pic;
3092 ibuf = ConvertBuf->buf;
3093 ibuflen = ConvertBuf->BufUsed;
3095 obuflen = TmpBuf->BufSize;
3097 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3100 if (errno == E2BIG) {
3102 IncreaseBuf(TmpBuf, 0, 0);
3107 else if (errno == EILSEQ){
3108 /* hm, invalid utf8 sequence... what to do now? */
3109 /* An invalid multibyte sequence has been encountered in the input */
3111 else if (errno == EINVAL) {
3112 /* An incomplete multibyte sequence has been encountered in the input. */
3115 FlushStrBuf(TmpBuf);
3118 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3119 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3121 /* little card game: wheres the red lady? */
3122 SwapBuffers(ConvertBuf, TmpBuf);
3123 FlushStrBuf(TmpBuf);
3130 * @ingroup StrBuf_DeEnCoder
3131 * @brief catches one RFC822 encoded segment, and decodes it.
3132 * @param Target buffer to fill with result
3133 * @param DecodeMe buffer with stuff to process
3134 * @param SegmentStart points to our current segment in DecodeMe
3135 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3136 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3137 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3138 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3140 inline static void DecodeSegment(StrBuf *Target,
3141 const StrBuf *DecodeMe,
3142 const char *SegmentStart,
3143 const char *SegmentEnd,
3145 StrBuf *ConvertBuf2,
3146 StrBuf *FoundCharset)
3152 iconv_t ic = (iconv_t)(-1);
3156 /* Now we handle foreign character sets properly encoded
3157 * in RFC2047 format.
3159 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3160 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3161 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3162 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3163 if (FoundCharset != NULL) {
3164 FlushStrBuf(FoundCharset);
3165 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3167 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3168 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3170 *encoding = toupper(*encoding);
3171 if (*encoding == 'B') { /**< base64 */
3172 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3173 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3174 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3176 ConvertBuf->BufUsed);
3178 else if (*encoding == 'Q') { /**< quoted-printable */
3182 while (pos < ConvertBuf->BufUsed)
3184 if (ConvertBuf->buf[pos] == '_')
3185 ConvertBuf->buf[pos] = ' ';
3189 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3190 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3192 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3195 ConvertBuf->BufUsed);
3198 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3201 ctdl_iconv_open("UTF-8", charset, &ic);
3202 if (ic != (iconv_t)(-1) ) {
3204 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3205 StrBufAppendBuf(Target, ConvertBuf2, 0);
3210 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3216 * @ingroup StrBuf_DeEnCoder
3217 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3218 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3219 * @param Target where to put the decoded string to
3220 * @param DecodeMe buffer with encoded string
3221 * @param DefaultCharset if we don't find one, which should we use?
3222 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3223 * put it here for later use where no string might be known.
3225 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3228 StrBuf *ConvertBuf2;
3229 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3230 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3232 StrBuf_RFC822_2_Utf8(Target,
3238 FreeStrBuf(&ConvertBuf);
3239 FreeStrBuf(&ConvertBuf2);
3243 * @ingroup StrBuf_DeEnCoder
3244 * @brief Handle subjects with RFC2047 encoding such as:
3245 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3246 * @param Target where to put the decoded string to
3247 * @param DecodeMe buffer with encoded string
3248 * @param DefaultCharset if we don't find one, which should we use?
3249 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3250 * put it here for later use where no string might be known.
3251 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3252 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3254 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3255 const StrBuf *DecodeMe,
3256 const StrBuf* DefaultCharset,
3257 StrBuf *FoundCharset,
3259 StrBuf *ConvertBuf2)
3261 StrBuf *DecodedInvalidBuf = NULL;
3262 const StrBuf *DecodeMee = DecodeMe;
3263 const char *start, *end, *next, *nextend, *ptr = NULL;
3265 iconv_t ic = (iconv_t)(-1) ;
3270 int illegal_non_rfc2047_encoding = 0;
3272 /* Sometimes, badly formed messages contain strings which were simply
3273 * written out directly in some foreign character set instead of
3274 * using RFC2047 encoding. This is illegal but we will attempt to
3275 * handle it anyway by converting from a user-specified default
3276 * charset to UTF-8 if we see any nonprintable characters.
3279 len = StrLength(DecodeMe);
3280 for (i=0; i<DecodeMe->BufUsed; ++i) {
3281 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3282 illegal_non_rfc2047_encoding = 1;
3287 if ((illegal_non_rfc2047_encoding) &&
3288 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3289 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3292 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3293 if (ic != (iconv_t)(-1) ) {
3294 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3295 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3296 DecodeMee = DecodedInvalidBuf;
3302 /* pre evaluate the first pair */
3303 nextend = end = NULL;
3304 len = StrLength(DecodeMee);
3305 start = strstr(DecodeMee->buf, "=?");
3306 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3308 end = FindNextEnd (DecodeMee, start + 2);
3310 StrBufAppendBuf(Target, DecodeMee, 0);
3311 FreeStrBuf(&DecodedInvalidBuf);
3316 if (start != DecodeMee->buf) {
3319 nFront = start - DecodeMee->buf;
3320 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3324 * Since spammers will go to all sorts of absurd lengths to get their
3325 * messages through, there are LOTS of corrupt headers out there.
3326 * So, prevent a really badly formed RFC2047 header from throwing
3327 * this function into an infinite loop.
3329 while ((start != NULL) &&
3336 DecodeSegment(Target,
3344 next = strstr(end, "=?");
3346 if ((next != NULL) &&
3348 nextend = FindNextEnd(DecodeMee, next);
3349 if (nextend == NULL)
3352 /* did we find two partitions */
3353 if ((next != NULL) &&
3357 while ((ptr < next) &&
3364 * did we find a gab just filled with blanks?
3365 * if not, copy its stuff over.
3369 StrBufAppendBufPlain(Target,
3375 /* our next-pair is our new first pair now. */
3381 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3382 if ((end != NULL) && (end < nextend)) {
3384 while ( (ptr < nextend) &&
3391 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3393 FreeStrBuf(&DecodedInvalidBuf);
3396 /*******************************************************************************
3397 * Manipulating UTF-8 Strings *
3398 *******************************************************************************/
3402 * @brief evaluate the length of an utf8 special character sequence
3403 * @param Char the character to examine
3404 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3406 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3409 unsigned char test = (1<<7);
3411 if ((*CharS & 0xC0) != 0xC0)
3415 ((test & ((unsigned char)*CharS)) != 0))
3420 if ((n > 6) || ((CharE - CharS) < n))
3427 * @brief detect whether this char starts an utf-8 encoded char
3428 * @param Char character to inspect
3429 * @returns yes or no
3431 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3433 /** 11??.???? indicates an UTF8 Sequence. */
3434 return ((Char & 0xC0) == 0xC0);
3439 * @brief measure the number of glyphs in an UTF8 string...
3440 * @param Buf string to measure
3441 * @returns the number of glyphs in Buf
3443 long StrBuf_Utf8StrLen(StrBuf *Buf)
3449 if ((Buf == NULL) || (Buf->BufUsed == 0))
3452 eptr = Buf->buf + Buf->BufUsed;
3453 while ((aptr < eptr) && (*aptr != '\0')) {
3454 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3455 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3456 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3469 * @brief cuts a string after maxlen glyphs
3470 * @param Buf string to cut to maxlen glyphs
3471 * @param maxlen how long may the string become?
3472 * @returns current length of the string
3474 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3480 eptr = Buf->buf + Buf->BufUsed;
3481 while ((aptr < eptr) && (*aptr != '\0')) {
3482 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3483 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3484 while ((*aptr++ != '\0') && (m-- > 0));
3493 Buf->BufUsed = aptr - Buf->buf;
3494 return Buf->BufUsed;
3497 return Buf->BufUsed;
3505 /*******************************************************************************
3507 *******************************************************************************/
3510 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3511 #define OS_CODE 0x03 /*< unix */
3514 * @ingroup StrBuf_DeEnCoder
3515 * @brief uses the same calling syntax as compress2(), but it
3516 * creates a stream compatible with HTTP "Content-encoding: gzip"
3517 * @param dest compressed buffer
3518 * @param destLen length of the compresed data
3519 * @param source source to encode
3520 * @param sourceLen length of source to encode
3521 * @param level compression level
3523 int ZEXPORT compress_gzip(Bytef * dest,
3525 const Bytef * source,
3529 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3531 /* write gzip header */
3532 snprintf((char *) dest, *destLen,
3533 "%c%c%c%c%c%c%c%c%c%c",
3534 gz_magic[0], gz_magic[1], Z_DEFLATED,
3535 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3538 /* normal deflate */
3541 stream.next_in = (Bytef *) source;
3542 stream.avail_in = (uInt) sourceLen;
3543 stream.next_out = dest + 10L; // after header
3544 stream.avail_out = (uInt) * destLen;
3545 if ((uLong) stream.avail_out != *destLen)
3548 stream.zalloc = (alloc_func) 0;
3549 stream.zfree = (free_func) 0;
3550 stream.opaque = (voidpf) 0;
3552 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3553 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3557 err = deflate(&stream, Z_FINISH);
3558 if (err != Z_STREAM_END) {
3559 deflateEnd(&stream);
3560 return err == Z_OK ? Z_BUF_ERROR : err;
3562 *destLen = stream.total_out + 10L;
3564 /* write CRC and Length */
3565 uLong crc = crc32(0L, source, sourceLen);
3567 for (n = 0; n < 4; ++n, ++*destLen) {
3568 dest[*destLen] = (int) (crc & 0xff);
3571 uLong len = stream.total_in;
3572 for (n = 0; n < 4; ++n, ++*destLen) {
3573 dest[*destLen] = (int) (len & 0xff);
3576 err = deflateEnd(&stream);
3583 * @ingroup StrBuf_DeEnCoder
3584 * @brief compress the buffer with gzip
3585 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3586 * @param Buf buffer whose content is to be gzipped
3588 int CompressBuffer(StrBuf *Buf)
3591 char *compressed_data = NULL;
3592 size_t compressed_len, bufsize;
3595 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3596 compressed_data = malloc(compressed_len);
3598 if (compressed_data == NULL)
3600 /* Flush some space after the used payload so valgrind shuts up... */
3601 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3602 Buf->buf[Buf->BufUsed + i++] = '\0';
3603 if (compress_gzip((Bytef *) compressed_data,
3606 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3609 Buf->buf = compressed_data;
3610 Buf->BufUsed = compressed_len;
3611 Buf->BufSize = bufsize;
3612 /* Flush some space after the used payload so valgrind shuts up... */
3614 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3615 Buf->buf[Buf->BufUsed + i++] = '\0';
3618 free(compressed_data);
3620 #endif /* HAVE_ZLIB */
3624 /*******************************************************************************
3625 * File I/O; Callbacks to libevent *
3626 *******************************************************************************/
3628 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3633 if ((FB == NULL) || (FB->Buf == NULL))
3637 * check whether the read pointer is somewhere in a range
3638 * where a cut left is inexpensive
3641 if (FB->ReadWritePointer != NULL)
3645 already_read = FB->ReadWritePointer - FB->Buf->buf;
3646 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3648 if (already_read != 0) {
3651 unread = FB->Buf->BufUsed - already_read;
3653 /* else nothing to compact... */
3655 FB->ReadWritePointer = FB->Buf->buf;
3656 bufremain = FB->Buf->BufSize;
3658 else if ((unread < 64) ||
3659 (bufremain < already_read))
3662 * if its just a tiny bit remaining, or we run out of space...
3665 FB->Buf->BufUsed = unread;
3666 if (unread < already_read)
3667 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3669 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3670 FB->ReadWritePointer = FB->Buf->buf;
3671 bufremain = FB->Buf->BufSize - unread - 1;
3673 else if (bufremain < (FB->Buf->BufSize / 10))
3675 /* get a bigger buffer */
3677 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3679 FB->ReadWritePointer = FB->Buf->buf + unread;
3681 bufremain = FB->Buf->BufSize - unread - 1;
3682 /*TODO: special increase function that won't copy the already read! */
3685 else if (bufremain < 10) {
3686 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3688 FB->ReadWritePointer = FB->Buf->buf;
3690 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3695 FB->ReadWritePointer = FB->Buf->buf;
3696 bufremain = FB->Buf->BufSize - 1;
3699 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3702 FB->Buf->BufUsed += n;
3703 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3708 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3713 if ((FB == NULL) || (FB->Buf == NULL))
3716 if (FB->ReadWritePointer != NULL)
3718 WriteRemain = FB->Buf->BufUsed -
3719 (FB->ReadWritePointer -
3723 FB->ReadWritePointer = FB->Buf->buf;
3724 WriteRemain = FB->Buf->BufUsed;
3727 n = write(fd, FB->ReadWritePointer, WriteRemain);
3729 FB->ReadWritePointer += n;
3731 if (FB->ReadWritePointer ==
3732 FB->Buf->buf + FB->Buf->BufUsed)
3734 FlushStrBuf(FB->Buf);
3735 FB->ReadWritePointer = NULL;
3738 // check whether we've got something to write
3739 // get the maximum chunk plus the pointer we can send
3740 // write whats there
3741 // if not all was sent, remember the send pointer for the next time
3742 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3748 * @ingroup StrBuf_IO
3749 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3750 * @param LineBuf your line will be copied here.
3751 * @param FB BLOB with lines of text...
3752 * @param Ptr moved arround to keep the next-line across several iterations
3753 * has to be &NULL on start; will be &NotNULL on end of buffer
3754 * @returns size of copied buffer
3756 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3758 const char *aptr, *ptr, *eptr;
3761 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3762 FB->ReadWritePointer = StrBufNOTNULL;
3766 FlushStrBuf(LineBuf);
3767 if (FB->ReadWritePointer == NULL)
3768 ptr = aptr = FB->Buf->buf;
3770 ptr = aptr = FB->ReadWritePointer;
3772 optr = LineBuf->buf;
3773 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3774 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3776 while ((ptr <= eptr) &&
3783 LineBuf->BufUsed = optr - LineBuf->buf;
3784 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3785 optr = LineBuf->buf + LineBuf->BufUsed;
3786 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3791 if (optr > LineBuf->buf)
3793 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3794 LineBuf->BufUsed = optr - LineBuf->buf;
3796 if ((FB->ReadWritePointer != NULL) &&
3797 (FB->ReadWritePointer != FB->Buf->buf))
3799 /* Ok, the client application read all the data
3800 it was interested in so far. Since there is more to read,
3801 we now shrink the buffer, and move the rest over.
3803 StrBufCutLeft(FB->Buf,
3804 FB->ReadWritePointer - FB->Buf->buf);
3805 FB->ReadWritePointer = FB->Buf->buf;
3807 return eMustReadMore;
3810 LineBuf->BufUsed = optr - LineBuf->buf;
3812 if ((ptr <= eptr) && (*ptr == '\r'))
3814 if ((ptr <= eptr) && (*ptr == '\n'))
3818 FB->ReadWritePointer = ptr;
3821 FlushStrBuf(FB->Buf);
3822 FB->ReadWritePointer = NULL;
3825 return eReadSuccess;
3829 * @ingroup StrBuf_CHUNKED_IO
3830 * @brief check whether the chunk-buffer has more data waiting or not.
3831 * @param FB Chunk-Buffer to inspect
3833 eReadState StrBufCheckBuffer(IOBuffer *FB)
3837 if (FB->Buf->BufUsed == 0)
3838 return eReadSuccess;
3839 if (FB->ReadWritePointer == NULL)
3840 return eBufferNotEmpty;
3841 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3842 return eBufferNotEmpty;
3843 return eReadSuccess;
3846 long IOBufferStrLength(IOBuffer *FB)
3848 if (FB->ReadWritePointer == NULL)
3849 return StrLength(FB->Buf);
3851 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3854 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3856 memset(FDB, 0, sizeof(FDIOBuffer));
3858 FDB->TotalSendSize = TotalSendSize;
3860 #ifndef LINUX_SENDFILE
3861 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3863 pipe(FDB->SplicePipe);
3868 void FDIOBufferDelete(FDIOBuffer *FDB)
3870 #ifndef LINUX_SENDFILE
3871 FreeStrBuf(&FDB->ChunkBuffer);
3873 close(FDB->SplicePipe[0]);
3874 close(FDB->SplicePipe[1]);
3877 close(FDB->OtherFD);
3878 memset(FDB, 0, sizeof(FDIOBuffer));
3881 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3886 #ifdef LINUX_SENDFILE
3888 sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3891 *Err = strerror(errno);
3894 FDB->ChunkSendRemain -= sent;
3895 FDB->TotalSentAlready += sent;
3896 return FDB->ChunkSendRemain;
3898 pRead = FDB->ChunkBuffer->buf;
3899 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3901 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3903 FDB->ChunkBuffer->BufUsed += nRead;
3904 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3906 else if (nRead == 0) {}
3911 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3914 FDB->TotalSentAlready += nRead;
3915 FDB->ChunkSendRemain -= nRead;
3916 return FDB->ChunkSendRemain;
3924 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3926 ssize_t sent, pipesize;
3928 #ifdef LINUX_SENDFILE
3930 pipesize = splice(FDB->IOB->fd, NULL,
3931 FDB->SplicePipe[1], NULL,
3932 FDB->ChunkSendRemain,
3933 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3936 *Err = strerror(errno);
3940 sent = splice(FDB->SplicePipe[0], NULL,
3941 FDB->OtherFD, &FDB->TotalSentAlready,
3942 pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
3945 *Err = strerror(errno);
3948 FDB->ChunkSendRemain -= sent;
3952 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
3957 FDB->ChunkBuffer->BufUsed = sent;
3959 while (nWritten < FDB->ChunkBuffer->BufUsed) {
3960 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
3962 *Err = strerror(errno);
3968 FDB->ChunkBuffer->BufUsed = 0;
3969 FDB->TotalSentAlready += sent;
3970 FDB->ChunkSendRemain -= sent;
3971 return FDB->ChunkSendRemain;
3973 else if (sent < 0) {
3974 *Err = strerror(errno);
3982 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
3988 int nSuccessLess = 0;
3992 fdflags = fcntl(FDB->OtherFD, F_GETFL);
3993 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3995 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
3996 (FDB->ChunkSendRemain > 0))
3999 tv.tv_sec = 1; /* selectresolution; */
4003 FD_SET(FDB->OtherFD, &rfds);
4004 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4005 *Error = strerror(errno);
4009 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4014 should_write = FDB->IOB->Buf->BufUsed -
4015 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4016 if (should_write > FDB->ChunkSendRemain)
4017 should_write = FDB->ChunkSendRemain;
4019 rlen = write(FDB->OtherFD,
4020 FDB->IOB->ReadWritePointer,
4023 *Error = strerror(errno);
4027 FDB->TotalSentAlready += rlen;
4028 FDB->IOB->ReadWritePointer += rlen;
4029 FDB->ChunkSendRemain -= rlen;
4031 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4033 FlushStrBuf(FDB->IOB->Buf);
4034 FDB->IOB->ReadWritePointer = NULL;
4037 if (FDB->ChunkSendRemain == 0)
4038 return eReadSuccess;
4040 return eMustReadMore;
4043 /*******************************************************************************
4044 * File I/O; Prefer buffered read since its faster! *
4045 *******************************************************************************/
4048 * @ingroup StrBuf_IO
4049 * @brief Read a line from socket
4050 * flushes and closes the FD on error
4051 * @param buf the buffer to get the input to
4052 * @param fd pointer to the filedescriptor to read
4053 * @param append Append to an existing string or replace?
4054 * @param Error strerror() on error
4055 * @returns numbers of chars read
4057 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4059 int len, rlen, slen;
4064 slen = len = buf->BufUsed;
4066 rlen = read(*fd, &buf->buf[len], 1);
4068 *Error = strerror(errno);
4075 if (buf->buf[len] == '\n')
4077 if (buf->buf[len] != '\r')
4079 if (len + 2 >= buf->BufSize) {
4081 buf->buf[len+1] = '\0';
4082 IncreaseBuf(buf, 1, -1);
4086 buf->buf[len] = '\0';
4091 * @ingroup StrBuf_BufferedIO
4092 * @brief Read a line from socket
4093 * flushes and closes the FD on error
4094 * @param Line the line to read from the fd / I/O Buffer
4095 * @param buf the buffer to get the input to
4096 * @param fd pointer to the filedescriptor to read
4097 * @param timeout number of successless selects until we bail out
4098 * @param selectresolution how long to wait on each select
4099 * @param Error strerror() on error
4100 * @returns numbers of chars read
4102 int StrBufTCP_read_buffered_line(StrBuf *Line,
4106 int selectresolution,
4110 int nSuccessLess = 0;
4117 if (buf->BufUsed > 0) {
4118 pch = strchr(buf->buf, '\n');
4121 len = pch - buf->buf;
4122 if (len > 0 && (*(pch - 1) == '\r') )
4124 StrBufSub(Line, buf, 0, len - rlen);
4125 StrBufCutLeft(buf, len + 1);
4130 if (buf->BufSize - buf->BufUsed < 10)
4131 IncreaseBuf(buf, 1, -1);
4133 fdflags = fcntl(*fd, F_GETFL);
4134 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4136 while ((nSuccessLess < timeout) && (pch == NULL)) {
4138 tv.tv_sec = selectresolution;
4143 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4144 *Error = strerror(errno);
4150 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4155 &buf->buf[buf->BufUsed],
4156 buf->BufSize - buf->BufUsed - 1);
4158 *Error = strerror(errno);
4163 else if (rlen > 0) {
4165 buf->BufUsed += rlen;
4166 buf->buf[buf->BufUsed] = '\0';
4167 if (buf->BufUsed + 10 > buf->BufSize) {
4168 IncreaseBuf(buf, 1, -1);
4170 pch = strchr(buf->buf, '\n');
4177 len = pch - buf->buf;
4178 if (len > 0 && (*(pch - 1) == '\r') )
4180 StrBufSub(Line, buf, 0, len - rlen);
4181 StrBufCutLeft(buf, len + 1);
4188 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4189 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4190 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4192 * @ingroup StrBuf_BufferedIO
4193 * @brief Read a line from socket
4194 * flushes and closes the FD on error
4195 * @param Line where to append our Line read from the fd / I/O Buffer;
4196 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4197 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4198 * @param fd pointer to the filedescriptor to read
4199 * @param timeout number of successless selects until we bail out
4200 * @param selectresolution how long to wait on each select
4201 * @param Error strerror() on error
4202 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4204 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4209 int selectresolution,
4212 const char *pche = NULL;
4213 const char *pos = NULL;
4215 int len, rlen, retlen;
4216 int nSuccessLess = 0;
4218 const char *pch = NULL;
4224 if ((Line == NULL) ||
4231 *Error = ErrRBLF_PreConditionFailed;
4236 if ((IOBuf->BufUsed > 0) &&
4238 (pos < IOBuf->buf + IOBuf->BufUsed))
4242 pche = IOBuf->buf + IOBuf->BufUsed;
4246 while ((pch < pche) && (*pch != '\n'))
4248 if (Line->BufUsed + 10 > Line->BufSize)
4251 apos = pcht - Line->buf;
4253 IncreaseBuf(Line, 1, -1);
4254 pcht = Line->buf + apos;
4262 if (len > 0 && (*(pch - 1) == '\r') )
4271 if ((pch >= pche) || (*pch == '\0'))
4279 if ((pch != NULL) &&
4282 if (pch + 1 >= pche) {
4295 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4297 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4298 IncreaseBuf(IOBuf, 1, -1);
4300 fdflags = fcntl(*fd, F_GETFL);
4301 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4304 while ((nSuccessLess < timeout) &&
4314 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4315 *Error = strerror(errno);
4319 *Error = ErrRBLF_SelectFailed;
4322 if (! FD_ISSET(*fd, &rfds) != 0) {
4328 &IOBuf->buf[IOBuf->BufUsed],
4329 IOBuf->BufSize - IOBuf->BufUsed - 1);
4331 *Error = strerror(errno);
4336 else if (rlen > 0) {
4338 pLF = IOBuf->buf + IOBuf->BufUsed;
4339 IOBuf->BufUsed += rlen;
4340 IOBuf->buf[IOBuf->BufUsed] = '\0';
4342 pche = IOBuf->buf + IOBuf->BufUsed;
4344 while ((pLF < pche) && (*pLF != '\n'))
4346 if ((pLF >= pche) || (*pLF == '\0'))
4349 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4353 if (pLF != NULL) apos = pLF - IOBuf->buf;
4354 IncreaseBuf(IOBuf, 1, -1);
4355 if (pLF != NULL) pLF = IOBuf->buf + apos;
4365 if (len > 0 && (*(pLF - 1) == '\r') )
4367 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4368 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4374 return retlen + len;
4376 *Error = ErrRBLF_NotEnoughSentFromServer;
4381 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4383 * @ingroup StrBuf_IO
4384 * @brief Input binary data from socket
4385 * flushes and closes the FD on error
4386 * @param Buf the buffer to get the input to
4387 * @param fd pointer to the filedescriptor to read
4388 * @param append Append to an existing string or replace?
4389 * @param nBytes the maximal number of bytes to read
4390 * @param Error strerror() on error
4391 * @returns numbers of chars read
4393 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4404 if ((Buf == NULL) || (*fd == -1))
4406 *Error = ErrRBLF_BLOBPreConditionFailed;
4411 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4412 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4414 ptr = Buf->buf + Buf->BufUsed;
4416 fdflags = fcntl(*fd, F_GETFL);
4417 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4419 while ((nRead < nBytes) &&
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) {
4443 if ((rlen = read(*fd,
4445 nBytes - nRead)) == -1) {
4448 *Error = strerror(errno);
4453 Buf->BufUsed += rlen;
4455 Buf->buf[Buf->BufUsed] = '\0';
4459 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4460 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4462 * @ingroup StrBuf_BufferedIO
4463 * @brief Input binary data from socket
4464 * flushes and closes the FD on error
4465 * @param Blob put binary thing here
4466 * @param IOBuf the buffer to get the input to
4467 * @param Pos offset inside of IOBuf
4468 * @param fd pointer to the filedescriptor to read
4469 * @param append Append to an existing string or replace?
4470 * @param nBytes the maximal number of bytes to read
4471 * @param check whether we should search for '000\n' terminators in case of timeouts
4472 * @param Error strerror() on error
4473 * @returns numbers of chars read
4475 int StrBufReadBLOBBuffered(StrBuf *Blob,
4489 int nAlreadyRead = 0;
4494 int nSuccessLess = 0;
4497 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4501 *Error = ErrRBB_BLOBFPreConditionFailed;
4507 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4508 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4513 len = pos - IOBuf->buf;
4514 rlen = IOBuf->BufUsed - len;
4517 if ((IOBuf->BufUsed > 0) &&
4519 (pos < IOBuf->buf + IOBuf->BufUsed))
4521 if (rlen < nBytes) {
4522 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4523 Blob->BufUsed += rlen;
4524 Blob->buf[Blob->BufUsed] = '\0';
4525 nAlreadyRead = nRead = rlen;
4528 if (rlen >= nBytes) {
4529 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4530 Blob->BufUsed += nBytes;
4531 Blob->buf[Blob->BufUsed] = '\0';
4532 if (rlen == nBytes) {
4544 if (IOBuf->BufSize < nBytes - nRead)
4545 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4548 len = Blob->BufUsed;
4550 fdflags = fcntl(*fd, F_GETFL);
4551 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4559 while ((nSuccessLess < MaxTries) &&
4569 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4570 *Error = strerror(errno);
4574 *Error = ErrRBLF_SelectFailed;
4577 if (! FD_ISSET(*fd, &rfds) != 0) {
4584 IOBuf->BufSize - (ptr - IOBuf->buf));
4588 *Error = strerror(errno);
4591 else if (rlen == 0){
4592 if ((check == NNN_TERM) &&
4594 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4596 StrBufPlain(Blob, HKEY("\n000\n"));
4597 StrBufCutRight(Blob, 5);
4598 return Blob->BufUsed;
4600 else if (!IsNonBlock)
4602 else if (nSuccessLess > MaxTries) {
4604 *Error = ErrRBB_too_many_selects;
4608 else if (rlen > 0) {
4612 IOBuf->BufUsed += rlen;
4615 if (nSuccessLess >= MaxTries) {
4617 *Error = ErrRBB_too_many_selects;
4621 if (nRead > nBytes) {
4622 *Pos = IOBuf->buf + nBytes;
4624 Blob->buf[Blob->BufUsed] = '\0';
4625 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4629 return nRead + nAlreadyRead;
4633 * @ingroup StrBuf_IO
4634 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4635 * @param LineBuf your line will be copied here.
4636 * @param Buf BLOB with lines of text...
4637 * @param Ptr moved arround to keep the next-line across several iterations
4638 * has to be &NULL on start; will be &NotNULL on end of buffer
4639 * @returns size of remaining buffer
4641 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4643 const char *aptr, *ptr, *eptr;
4646 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4647 *Ptr = StrBufNOTNULL;
4651 FlushStrBuf(LineBuf);
4653 ptr = aptr = Buf->buf;
4657 optr = LineBuf->buf;
4658 eptr = Buf->buf + Buf->BufUsed;
4659 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4661 while ((ptr <= eptr) &&
4668 LineBuf->BufUsed = optr - LineBuf->buf;
4669 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4670 optr = LineBuf->buf + LineBuf->BufUsed;
4671 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4675 if ((ptr >= eptr) && (optr > LineBuf->buf))
4677 LineBuf->BufUsed = optr - LineBuf->buf;
4679 if ((ptr <= eptr) && (*ptr == '\r'))
4681 if ((ptr <= eptr) && (*ptr == '\n'))
4688 *Ptr = StrBufNOTNULL;
4691 return Buf->BufUsed - (ptr - Buf->buf);
4696 * @ingroup StrBuf_IO
4697 * @brief removes double slashes from pathnames
4698 * @param Dir directory string to filter
4699 * @param RemoveTrailingSlash allows / disallows trailing slashes
4701 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4707 while (!IsEmptyStr(a)) {
4719 if ((RemoveTrailingSlash) &&
4725 Dir->BufUsed = b - Dir->buf;