2 * Copyright (c) 1987-2011 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/select.h>
29 #include <sys/types.h>
30 #define SHOW_ME_VAPPEND_PRINTF
33 #include "libcitadel.h"
44 #include <sys/sendfile.h>
49 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
50 const Bytef * source, uLong sourceLen, int level);
52 int BaseStrBufSize = 64;
54 const char *StrBufNOTNULL = ((char*) NULL) - 1;
56 const char HexList[256][3] = {
57 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
58 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
59 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
60 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
61 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
62 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
63 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
64 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
65 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
66 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
67 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
68 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
69 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
70 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
71 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
72 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
75 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
76 * StrBuf is a versatile class, aiding the handling of dynamic strings
77 * * reduce de/reallocations
78 * * reduce the need to remeasure it
79 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
80 * * allow asyncroneous IO for line and Blob based operations
81 * * reduce the use of memove in those
82 * * Quick filling in several operations with append functions
86 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
91 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
93 * use these operators to interfere with code demanding char*;
94 * if you need to own the content, smash me. Avoid, since we loose the length information.
98 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
100 * operations to get your Strings into a StrBuf, manipulating them, or appending
103 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
105 * Quick tokenizer; demands of the user to pull its tokens in sequence
109 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
111 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
115 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
117 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
118 * External Cursor to maintain the current read position inside of the buffer
119 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
123 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
129 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
131 * these functions translate the content of a buffer into another representation;
132 * some are combined Fillers and encoders
136 * Private Structure for the Stringbuffer
139 char *buf; /**< the pointer to the dynamic buffer */
140 long BufSize; /**< how many spcae do we optain */
141 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
142 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
144 long nIncreases; /**< for profiling; cound how many times we needed more */
145 char bt [SIZ]; /**< Stacktrace of last increase */
146 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
151 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
152 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
155 #ifdef HAVE_BACKTRACE
156 static void StrBufBacktrace(StrBuf *Buf, int which)
160 void *stack_frames[50];
165 pstart = pch = Buf->bt;
167 pstart = pch = Buf->bt_lastinc;
168 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
169 strings = backtrace_symbols(stack_frames, size);
170 for (i = 0; i < size; i++) {
172 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
174 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
183 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
185 if (hFreeDbglog == -1){
186 pid_t pid = getpid();
188 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
189 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
191 if ((*FreeMe)->nIncreases > 0)
195 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
197 (*FreeMe)->nIncreases,
201 (*FreeMe)->bt_lastinc);
202 n = write(hFreeDbglog, buf, n);
208 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
212 n = write(hFreeDbglog, buf, n);
216 void dbg_IncreaseBuf(StrBuf *IncMe)
219 #ifdef HAVE_BACKTRACE
220 StrBufBacktrace(Buf, 1);
224 void dbg_Init(StrBuf *Buf)
228 Buf->bt_lastinc[0] = '\0';
229 #ifdef HAVE_BACKTRACE
230 StrBufBacktrace(Buf, 0);
236 #define dbg_FreeStrBuf(a, b)
237 #define dbg_IncreaseBuf(a)
244 * @brief swaps the contents of two StrBufs
245 * this is to be used to have cheap switched between a work-buffer and a target buffer
247 * @param B second one
249 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
253 memcpy(&C, A, sizeof(*A));
254 memcpy(A, B, sizeof(*B));
255 memcpy(B, &C, sizeof(C));
260 * @ingroup StrBuf_Cast
261 * @brief Cast operator to Plain String
262 * @note if the buffer is altered by StrBuf operations, this pointer may become
263 * invalid. So don't lean on it after altering the buffer!
264 * Since this operation is considered cheap, rather call it often than risking
265 * your pointer to become invalid!
266 * @param Str the string we want to get the c-string representation for
267 * @returns the Pointer to the Content. Don't mess with it!
269 inline const char *ChrPtr(const StrBuf *Str)
277 * @ingroup StrBuf_Cast
278 * @brief since we know strlen()'s result, provide it here.
279 * @param Str the string to return the length to
280 * @returns contentlength of the buffer
282 inline int StrLength(const StrBuf *Str)
284 return (Str != NULL) ? Str->BufUsed : 0;
288 * @ingroup StrBuf_DeConstructors
289 * @brief local utility function to resize the buffer
290 * @param Buf the buffer whichs storage we should increase
291 * @param KeepOriginal should we copy the original buffer or just start over with a new one
292 * @param DestSize what should fit in after?
294 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
297 size_t NewSize = Buf->BufSize * 2;
303 while ((NewSize <= DestSize) && (NewSize != 0))
309 NewBuf= (char*) malloc(NewSize);
313 if (KeepOriginal && (Buf->BufUsed > 0))
315 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
324 Buf->BufSize = NewSize;
326 dbg_IncreaseBuf(Buf);
332 * @ingroup StrBuf_DeConstructors
333 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
334 * @param Buf Buffer to shrink (has to be empty)
335 * @param ThreshHold if the buffer is bigger then this, its readjusted
336 * @param NewSize if we Shrink it, how big are we going to be afterwards?
338 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
341 (Buf->BufUsed == 0) &&
342 (Buf->BufSize < ThreshHold)) {
344 Buf->buf = (char*) malloc(NewSize);
346 Buf->BufSize = NewSize;
351 * @ingroup StrBuf_DeConstructors
352 * @brief shrink long term buffers to their real size so they don't waste memory
353 * @param Buf buffer to shrink
354 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
355 * @returns physical size of the buffer
357 long StrBufShrinkToFit(StrBuf *Buf, int Force)
362 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
364 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
365 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
366 Buf->BufSize = Buf->BufUsed + 1;
374 * @ingroup StrBuf_DeConstructors
375 * @brief Allocate a new buffer with default buffer size
376 * @returns the new stringbuffer
378 StrBuf* NewStrBuf(void)
382 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
383 NewBuf->buf = (char*) malloc(BaseStrBufSize);
384 NewBuf->buf[0] = '\0';
385 NewBuf->BufSize = BaseStrBufSize;
387 NewBuf->ConstBuf = 0;
395 * @ingroup StrBuf_DeConstructors
396 * @brief Copy Constructor; returns a duplicate of CopyMe
397 * @param CopyMe Buffer to faxmilate
398 * @returns the new stringbuffer
400 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
407 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
408 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
409 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
410 NewBuf->BufUsed = CopyMe->BufUsed;
411 NewBuf->BufSize = CopyMe->BufSize;
412 NewBuf->ConstBuf = 0;
420 * @ingroup StrBuf_DeConstructors
421 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
422 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
423 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
424 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
425 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
426 * @returns the new stringbuffer
428 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
432 if (CreateRelpaceMe == NULL)
437 if (*CreateRelpaceMe != NULL)
438 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
440 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
444 if (CopyFlushMe == NULL)
446 if (*CreateRelpaceMe != NULL)
447 FlushStrBuf(*CreateRelpaceMe);
449 *CreateRelpaceMe = NewStrBuf();
454 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
455 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
456 * be a big IO-Buffer...
458 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
460 if (*CreateRelpaceMe == NULL)
462 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
467 NewBuf = *CreateRelpaceMe;
470 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
474 if (*CreateRelpaceMe == NULL)
476 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
480 NewBuf = *CreateRelpaceMe;
481 SwapBuffers (NewBuf, CopyFlushMe);
484 FlushStrBuf(CopyFlushMe);
489 * @ingroup StrBuf_DeConstructors
490 * @brief create a new Buffer using an existing c-string
491 * this function should also be used if you want to pre-suggest
492 * the buffer size to allocate in conjunction with ptr == NULL
493 * @param ptr the c-string to copy; may be NULL to create a blank instance
494 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
495 * @returns the new stringbuffer
497 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
500 size_t Siz = BaseStrBufSize;
503 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
505 CopySize = strlen((ptr != NULL)?ptr:"");
509 while ((Siz <= CopySize) && (Siz != 0))
517 NewBuf->buf = (char*) malloc(Siz);
518 if (NewBuf->buf == NULL)
523 NewBuf->BufSize = Siz;
525 memcpy(NewBuf->buf, ptr, CopySize);
526 NewBuf->buf[CopySize] = '\0';
527 NewBuf->BufUsed = CopySize;
530 NewBuf->buf[0] = '\0';
533 NewBuf->ConstBuf = 0;
541 * @ingroup StrBuf_DeConstructors
542 * @brief Set an existing buffer from a c-string
543 * @param Buf buffer to load
544 * @param ptr c-string to put into
545 * @param nChars set to -1 if we should work 0-terminated
546 * @returns the new length of the string
548 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
563 CopySize = strlen(ptr);
567 while ((Siz <= CopySize) && (Siz != 0))
575 if (Siz != Buf->BufSize)
576 IncreaseBuf(Buf, 0, Siz);
577 memcpy(Buf->buf, ptr, CopySize);
578 Buf->buf[CopySize] = '\0';
579 Buf->BufUsed = CopySize;
586 * @ingroup StrBuf_DeConstructors
587 * @brief use strbuf as wrapper for a string constant for easy handling
588 * @param StringConstant a string to wrap
589 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
591 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
595 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
596 NewBuf->buf = (char*) StringConstant;
597 NewBuf->BufSize = SizeOfStrConstant;
598 NewBuf->BufUsed = SizeOfStrConstant;
599 NewBuf->ConstBuf = 1;
608 * @ingroup StrBuf_DeConstructors
609 * @brief flush the content of a Buf; keep its struct
610 * @param buf Buffer to flush
612 int FlushStrBuf(StrBuf *buf)
624 * @ingroup StrBuf_DeConstructors
625 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
626 * @param buf Buffer to wipe
628 int FLUSHStrBuf(StrBuf *buf)
634 if (buf->BufUsed > 0) {
635 memset(buf->buf, 0, buf->BufUsed);
642 int hFreeDbglog = -1;
645 * @ingroup StrBuf_DeConstructors
646 * @brief Release a Buffer
647 * Its a double pointer, so it can NULL your pointer
648 * so fancy SIG11 appear instead of random results
649 * @param FreeMe Pointer Pointer to the buffer to free
651 void FreeStrBuf (StrBuf **FreeMe)
656 dbg_FreeStrBuf(FreeMe, 'F');
658 if (!(*FreeMe)->ConstBuf)
659 free((*FreeMe)->buf);
665 * @ingroup StrBuf_DeConstructors
666 * @brief flatten a Buffer to the Char * we return
667 * Its a double pointer, so it can NULL your pointer
668 * so fancy SIG11 appear instead of random results
669 * The Callee then owns the buffer and is responsible for freeing it.
670 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
671 * @returns the pointer of the buffer; Callee owns the memory thereafter.
673 char *SmashStrBuf (StrBuf **SmashMe)
677 if ((SmashMe == NULL) || (*SmashMe == NULL))
680 dbg_FreeStrBuf(SmashMe, 'S');
682 Ret = (*SmashMe)->buf;
689 * @ingroup StrBuf_DeConstructors
690 * @brief Release the buffer
691 * If you want put your StrBuf into a Hash, use this as Destructor.
692 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
694 void HFreeStrBuf (void *VFreeMe)
696 StrBuf *FreeMe = (StrBuf*)VFreeMe;
700 dbg_FreeStrBuf(SmashMe, 'H');
702 if (!FreeMe->ConstBuf)
708 /*******************************************************************************
709 * Simple string transformations *
710 *******************************************************************************/
714 * @brief Wrapper around atol
716 long StrTol(const StrBuf *Buf)
721 return atol(Buf->buf);
728 * @brief Wrapper around atoi
730 int StrToi(const StrBuf *Buf)
734 if (Buf->BufUsed > 0)
735 return atoi(Buf->buf);
742 * @brief Checks to see if the string is a pure number
743 * @param Buf The buffer to inspect
744 * @returns 1 if its a pure number, 0, if not.
746 int StrBufIsNumber(const StrBuf *Buf) {
748 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
751 strtoll(Buf->buf, &pEnd, 10);
752 if (pEnd == Buf->buf)
754 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
756 if (Buf->buf == pEnd)
762 * @ingroup StrBuf_Filler
763 * @brief modifies a Single char of the Buf
764 * You can point to it via char* or a zero-based integer
765 * @param Buf The buffer to manipulate
766 * @param ptr char* to zero; use NULL if unused
767 * @param nThChar zero based pointer into the string; use -1 if unused
768 * @param PeekValue The Character to place into the position
770 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
775 nThChar = ptr - Buf->buf;
776 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
778 Buf->buf[nThChar] = PeekValue;
783 * @ingroup StrBuf_Filler
784 * @brief modifies a range of chars of the Buf
785 * You can point to it via char* or a zero-based integer
786 * @param Buf The buffer to manipulate
787 * @param ptr char* to zero; use NULL if unused
788 * @param nThChar zero based pointer into the string; use -1 if unused
789 * @param nChars how many chars are to be flushed?
790 * @param PookValue The Character to place into that area
792 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
797 nThChar = ptr - Buf->buf;
798 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
800 if (nThChar + nChars > Buf->BufUsed)
801 nChars = Buf->BufUsed - nThChar;
803 memset(Buf->buf + nThChar, PookValue, nChars);
804 /* just to be shure... */
805 Buf->buf[Buf->BufUsed] = 0;
810 * @ingroup StrBuf_Filler
811 * @brief Append a StringBuffer to the buffer
812 * @param Buf Buffer to modify
813 * @param AppendBuf Buffer to copy at the end of our buffer
814 * @param Offset Should we start copying from an offset?
816 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
818 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
821 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
824 AppendBuf->BufUsed + Buf->BufUsed);
826 memcpy(Buf->buf + Buf->BufUsed,
827 AppendBuf->buf + Offset,
828 AppendBuf->BufUsed - Offset);
829 Buf->BufUsed += AppendBuf->BufUsed - Offset;
830 Buf->buf[Buf->BufUsed] = '\0';
835 * @ingroup StrBuf_Filler
836 * @brief Append a C-String to the buffer
837 * @param Buf Buffer to modify
838 * @param AppendBuf Buffer to copy at the end of our buffer
839 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
840 * @param Offset Should we start copying from an offset?
842 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
845 long BufSizeRequired;
847 if ((AppendBuf == NULL) || (Buf == NULL))
851 aps = strlen(AppendBuf + Offset);
853 aps = AppendSize - Offset;
855 BufSizeRequired = Buf->BufUsed + aps + 1;
856 if (Buf->BufSize <= BufSizeRequired)
857 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
859 memcpy(Buf->buf + Buf->BufUsed,
863 Buf->buf[Buf->BufUsed] = '\0';
867 * @ingroup StrBuf_Filler
868 * @brief sprintf like function appending the formated string to the buffer
869 * vsnprintf version to wrap into own calls
870 * @param Buf Buffer to extend by format and Params
871 * @param format printf alike format to add
872 * @param ap va_list containing the items for format
874 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
882 if ((Buf == NULL) || (format == NULL))
885 BufSize = Buf->BufSize;
886 nWritten = Buf->BufSize + 1;
887 Offset = Buf->BufUsed;
888 newused = Offset + nWritten;
890 while (newused >= BufSize) {
892 nWritten = vsnprintf(Buf->buf + Offset,
893 Buf->BufSize - Offset,
896 newused = Offset + nWritten;
897 if (newused >= Buf->BufSize) {
898 if (IncreaseBuf(Buf, 1, newused) == -1)
899 return; /* TODO: error handling? */
900 newused = Buf->BufSize + 1;
903 Buf->BufUsed = Offset + nWritten;
904 BufSize = Buf->BufSize;
911 * @ingroup StrBuf_Filler
912 * @brief sprintf like function appending the formated string to the buffer
913 * @param Buf Buffer to extend by format and Params
914 * @param format printf alike format to add
916 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
924 if ((Buf == NULL) || (format == NULL))
927 BufSize = Buf->BufSize;
928 nWritten = Buf->BufSize + 1;
929 Offset = Buf->BufUsed;
930 newused = Offset + nWritten;
932 while (newused >= BufSize) {
933 va_start(arg_ptr, format);
934 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
935 Buf->BufSize - Buf->BufUsed,
938 newused = Buf->BufUsed + nWritten;
939 if (newused >= Buf->BufSize) {
940 if (IncreaseBuf(Buf, 1, newused) == -1)
941 return; /* TODO: error handling? */
942 newused = Buf->BufSize + 1;
945 Buf->BufUsed += nWritten;
946 BufSize = Buf->BufSize;
953 * @ingroup StrBuf_Filler
954 * @brief sprintf like function putting the formated string into the buffer
955 * @param Buf Buffer to extend by format and Parameters
956 * @param format printf alike format to add
958 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
963 if ((Buf == NULL) || (format == NULL))
966 nWritten = Buf->BufSize + 1;
967 while (nWritten >= Buf->BufSize) {
968 va_start(arg_ptr, format);
969 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
971 if (nWritten >= Buf->BufSize) {
972 if (IncreaseBuf(Buf, 0, 0) == -1)
973 return; /* TODO: error handling? */
974 nWritten = Buf->BufSize + 1;
977 Buf->BufUsed = nWritten ;
982 * @ingroup StrBuf_Filler
983 * @brief Callback for cURL to append the webserver reply to a buffer
984 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
985 * @param size pre-defined by the cURL API; see man 3 curl for mre info
986 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
987 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
989 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
998 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1005 * @brief extracts a substring from Source into dest
1006 * @param dest buffer to place substring into
1007 * @param Source string to copy substring from
1008 * @param Offset chars to skip from start
1009 * @param nChars number of chars to copy
1010 * @returns the number of chars copied; may be different from nChars due to the size of Source
1012 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1014 size_t NCharsRemain;
1015 if (Offset > Source->BufUsed)
1021 if (Offset + nChars < Source->BufUsed)
1023 if (nChars >= dest->BufSize)
1024 IncreaseBuf(dest, 0, nChars + 1);
1025 memcpy(dest->buf, Source->buf + Offset, nChars);
1026 dest->BufUsed = nChars;
1027 dest->buf[dest->BufUsed] = '\0';
1030 NCharsRemain = Source->BufUsed - Offset;
1031 if (NCharsRemain >= dest->BufSize)
1032 IncreaseBuf(dest, 0, NCharsRemain + 1);
1033 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1034 dest->BufUsed = NCharsRemain;
1035 dest->buf[dest->BufUsed] = '\0';
1036 return NCharsRemain;
1041 * @brief Cut nChars from the start of the string
1042 * @param Buf Buffer to modify
1043 * @param nChars how many chars should be skipped?
1045 void StrBufCutLeft(StrBuf *Buf, int nChars)
1047 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1048 if (nChars >= Buf->BufUsed) {
1052 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1053 Buf->BufUsed -= nChars;
1054 Buf->buf[Buf->BufUsed] = '\0';
1059 * @brief Cut the trailing n Chars from the string
1060 * @param Buf Buffer to modify
1061 * @param nChars how many chars should be trunkated?
1063 void StrBufCutRight(StrBuf *Buf, int nChars)
1065 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1066 if (nChars >= Buf->BufUsed) {
1070 Buf->BufUsed -= nChars;
1071 Buf->buf[Buf->BufUsed] = '\0';
1076 * @brief Cut the string after n Chars
1077 * @param Buf Buffer to modify
1078 * @param AfternChars after how many chars should we trunkate the string?
1079 * @param At if non-null and points inside of our string, cut it there.
1081 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1083 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1085 AfternChars = At - Buf->buf;
1088 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1090 Buf->BufUsed = AfternChars;
1091 Buf->buf[Buf->BufUsed] = '\0';
1097 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1098 * @param Buf the string to modify
1100 void StrBufTrim(StrBuf *Buf)
1103 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1105 while ((Buf->BufUsed > 0) &&
1106 isspace(Buf->buf[Buf->BufUsed - 1]))
1110 Buf->buf[Buf->BufUsed] = '\0';
1112 if (Buf->BufUsed == 0) return;
1114 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1117 if (delta > 0) StrBufCutLeft(Buf, delta);
1121 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1122 * @param Buf the string to modify
1124 void StrBufSpaceToBlank(StrBuf *Buf)
1128 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1131 pche = pch + Buf->BufUsed;
1140 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1146 if ((Buf == NULL) || (Buf->buf == NULL))
1148 pLeft = pBuff = Buf->buf;
1149 while (pBuff != NULL) {
1151 pBuff = strchr(pBuff, leftboundary);
1160 pRight = strchr(pBuff, rightboundary);
1162 StrBufCutAt(Buf, 0, pRight);
1164 StrBufCutLeft(Buf, pLeft - Buf->buf);
1169 * @ingroup StrBuf_Filler
1170 * @brief uppercase the contents of a buffer
1171 * @param Buf the buffer to translate
1173 void StrBufUpCase(StrBuf *Buf)
1177 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1180 pche = pch + Buf->BufUsed;
1181 while (pch < pche) {
1182 *pch = toupper(*pch);
1189 * @ingroup StrBuf_Filler
1190 * @brief lowercase the contents of a buffer
1191 * @param Buf the buffer to translate
1193 void StrBufLowerCase(StrBuf *Buf)
1197 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1200 pche = pch + Buf->BufUsed;
1201 while (pch < pche) {
1202 *pch = tolower(*pch);
1208 /*******************************************************************************
1209 * a tokenizer that kills, maims, and destroys *
1210 *******************************************************************************/
1213 * @ingroup StrBuf_Tokenizer
1214 * @brief Replace a token at a given place with a given length by another token with given length
1215 * @param Buf String where to work on
1216 * @param where where inside of the Buf is the search-token
1217 * @param HowLong How long is the token to be replaced
1218 * @param Repl Token to insert at 'where'
1219 * @param ReplLen Length of repl
1220 * @returns -1 if fail else length of resulting Buf
1222 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1223 const char *Repl, long ReplLen)
1226 if ((Buf == NULL) ||
1227 (where > Buf->BufUsed) ||
1228 (where + HowLong > Buf->BufUsed))
1231 if (where + ReplLen - HowLong > Buf->BufSize)
1232 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1235 memmove(Buf->buf + where + ReplLen,
1236 Buf->buf + where + HowLong,
1237 Buf->BufUsed - where - HowLong);
1239 memcpy(Buf->buf + where,
1242 Buf->BufUsed += ReplLen - HowLong;
1244 return Buf->BufUsed;
1248 * @ingroup StrBuf_Tokenizer
1249 * @brief Counts the numbmer of tokens in a buffer
1250 * @param source String to count tokens in
1251 * @param tok Tokenizer char to count
1252 * @returns numbers of tokenizer chars found
1254 int StrBufNum_tokens(const StrBuf *source, char tok)
1258 if ((source == NULL) || (source->BufUsed == 0))
1260 if ((source->BufUsed == 1) && (*source->buf == tok))
1264 pche = pch + source->BufUsed;
1275 * @ingroup StrBuf_Tokenizer
1276 * @brief a string tokenizer
1277 * @param Source StringBuffer to read into
1278 * @param parmnum n'th Parameter to remove
1279 * @param separator tokenizer character
1280 * @returns -1 if not found, else length of token.
1282 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1285 char *d, *s, *end; /* dest, source */
1288 /* Find desired @parameter */
1289 end = Source->buf + Source->BufUsed;
1291 while ((d <= end) &&
1294 /* End of string, bail! */
1299 if (*d == separator) {
1304 if ((d == NULL) || (d >= end))
1305 return 0; /* @Parameter not found */
1307 /* Find next @parameter */
1309 while ((s <= end) &&
1310 (*s && *s != separator))
1314 if (*s == separator)
1318 /* Hack and slash */
1323 memmove(d, s, Source->BufUsed - (s - Source->buf));
1324 Source->BufUsed += ReducedBy;
1325 Source->buf[Source->BufUsed] = '\0';
1327 else if (d == Source->buf) {
1329 Source->BufUsed = 0;
1333 Source->BufUsed += ReducedBy;
1346 * @ingroup StrBuf_Tokenizer
1347 * @brief a string tokenizer
1348 * @param dest Destination StringBuffer
1349 * @param Source StringBuffer to read into
1350 * @param parmnum n'th Parameter to extract
1351 * @param separator tokenizer character
1352 * @returns -1 if not found, else length of token.
1354 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1356 const char *s, *e; //* source * /
1357 int len = 0; //* running total length of extracted string * /
1358 int current_token = 0; //* token currently being processed * /
1361 dest->buf[0] = '\0';
1367 if ((Source == NULL) || (Source->BufUsed ==0)) {
1371 e = s + Source->BufUsed;
1374 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1376 while ((s < e) && !IsEmptyStr(s)) {
1377 if (*s == separator) {
1380 if (len >= dest->BufSize) {
1381 dest->BufUsed = len;
1382 if (IncreaseBuf(dest, 1, -1) < 0) {
1387 if ( (current_token == parmnum) &&
1388 (*s != separator)) {
1389 dest->buf[len] = *s;
1392 else if (current_token > parmnum) {
1398 dest->buf[len] = '\0';
1399 dest->BufUsed = len;
1401 if (current_token < parmnum) {
1402 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1405 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1414 * @ingroup StrBuf_Tokenizer
1415 * @brief a string tokenizer to fetch an integer
1416 * @param Source String containing tokens
1417 * @param parmnum n'th Parameter to extract
1418 * @param separator tokenizer character
1419 * @returns 0 if not found, else integer representation of the token
1421 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1431 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1438 * @ingroup StrBuf_Tokenizer
1439 * @brief a string tokenizer to fetch a long integer
1440 * @param Source String containing tokens
1441 * @param parmnum n'th Parameter to extract
1442 * @param separator tokenizer character
1443 * @returns 0 if not found, else long integer representation of the token
1445 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1455 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1463 * @ingroup StrBuf_Tokenizer
1464 * @brief a string tokenizer to fetch an unsigned long
1465 * @param Source String containing tokens
1466 * @param parmnum n'th Parameter to extract
1467 * @param separator tokenizer character
1468 * @returns 0 if not found, else unsigned long representation of the token
1470 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1481 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1485 return (unsigned long) atol(pnum);
1494 * @ingroup StrBuf_NextTokenizer
1495 * @brief a string tokenizer; Bounds checker
1496 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1497 * @param Source our tokenbuffer
1498 * @param pStart the token iterator pointer to inspect
1499 * @returns whether the revolving pointer is inside of the search range
1501 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1503 if ((Source == NULL) ||
1504 (*pStart == StrBufNOTNULL) ||
1505 (Source->BufUsed == 0))
1509 if (*pStart == NULL)
1513 else if (*pStart > Source->buf + Source->BufUsed)
1517 else if (*pStart <= Source->buf)
1526 * @ingroup StrBuf_NextTokenizer
1527 * @brief a string tokenizer
1528 * @param dest Destination StringBuffer
1529 * @param Source StringBuffer to read into
1530 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1531 * @param separator tokenizer
1532 * @returns -1 if not found, else length of token.
1534 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1536 const char *s; /* source */
1537 const char *EndBuffer; /* end stop of source buffer */
1538 int current_token = 0; /* token currently being processed */
1539 int len = 0; /* running total length of extracted string */
1541 if ((Source == NULL) ||
1542 (Source->BufUsed == 0) )
1544 *pStart = StrBufNOTNULL;
1550 EndBuffer = Source->buf + Source->BufUsed;
1554 dest->buf[0] = '\0';
1559 *pStart = EndBuffer + 1;
1563 if (*pStart == NULL)
1565 *pStart = Source->buf; /* we're starting to examine this buffer. */
1567 else if ((*pStart < Source->buf) ||
1568 (*pStart > EndBuffer ) )
1570 return -1; /* no more tokens to find. */
1574 /* start to find the next token */
1575 while ((s <= EndBuffer) &&
1576 (current_token == 0) )
1578 if (*s == separator)
1580 /* we found the next token */
1584 if (len >= dest->BufSize)
1586 /* our Dest-buffer isn't big enough, increase it. */
1587 dest->BufUsed = len;
1589 if (IncreaseBuf(dest, 1, -1) < 0) {
1590 /* WHUT? no more mem? bail out. */
1597 if ( (current_token == 0 ) && /* are we in our target token? */
1598 (!IsEmptyStr(s) ) &&
1599 (separator != *s) ) /* don't copy the token itself */
1601 dest->buf[len] = *s; /* Copy the payload */
1602 ++len; /* remember the bigger size. */
1608 /* did we reach the end? */
1609 if ((s > EndBuffer)) {
1610 EndBuffer = StrBufNOTNULL;
1611 *pStart = EndBuffer;
1614 *pStart = s; /* remember the position for the next run */
1617 /* sanitize our extracted token */
1618 dest->buf[len] = '\0';
1619 dest->BufUsed = len;
1626 * @ingroup StrBuf_NextTokenizer
1627 * @brief a string tokenizer
1628 * @param Source StringBuffer to read from
1629 * @param pStart pointer to the end of the last token. Feed with NULL.
1630 * @param separator tokenizer character
1631 * @param nTokens number of tokens to fastforward over
1632 * @returns -1 if not found, else length of token.
1634 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1636 const char *s, *EndBuffer; //* source * /
1637 int len = 0; //* running total length of extracted string * /
1638 int current_token = 0; //* token currently being processed * /
1640 if ((Source == NULL) ||
1641 (Source->BufUsed ==0)) {
1645 return Source->BufUsed;
1647 if (*pStart == NULL)
1648 *pStart = Source->buf;
1650 EndBuffer = Source->buf + Source->BufUsed;
1652 if ((*pStart < Source->buf) ||
1653 (*pStart > EndBuffer)) {
1661 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1663 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1664 if (*s == separator) {
1667 if (current_token >= nTokens) {
1679 * @ingroup StrBuf_NextTokenizer
1680 * @brief a string tokenizer to fetch an integer
1681 * @param Source StringBuffer to read from
1682 * @param pStart Cursor on the tokenstring
1683 * @param separator tokenizer character
1684 * @returns 0 if not found, else integer representation of the token
1686 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1696 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1703 * @ingroup StrBuf_NextTokenizer
1704 * @brief a string tokenizer to fetch a long integer
1705 * @param Source StringBuffer to read from
1706 * @param pStart Cursor on the tokenstring
1707 * @param separator tokenizer character
1708 * @returns 0 if not found, else long integer representation of the token
1710 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1720 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1728 * @ingroup StrBuf_NextTokenizer
1729 * @brief a string tokenizer to fetch an unsigned long
1730 * @param Source StringBuffer to read from
1731 * @param pStart Cursor on the tokenstring
1732 * @param separator tokenizer character
1733 * @returns 0 if not found, else unsigned long representation of the token
1735 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1746 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1750 return (unsigned long) atol(pnum);
1760 /*******************************************************************************
1761 * Escape Appending *
1762 *******************************************************************************/
1765 * @ingroup StrBuf_DeEnCoder
1766 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1767 * @param OutBuf the output buffer
1768 * @param In Buffer to encode
1769 * @param PlainIn way in from plain old c strings
1771 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1773 const char *pch, *pche;
1777 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1779 if (PlainIn != NULL) {
1780 len = strlen(PlainIn);
1786 pche = pch + In->BufUsed;
1793 pt = OutBuf->buf + OutBuf->BufUsed;
1794 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1796 while (pch < pche) {
1798 IncreaseBuf(OutBuf, 1, -1);
1799 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1800 pt = OutBuf->buf + OutBuf->BufUsed;
1803 if((*pch >= 'a' && *pch <= 'z') ||
1804 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1805 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1806 (*pch == '!') || (*pch == '_') ||
1807 (*pch == ',') || (*pch == '.'))
1814 *(pt + 1) = HexList[(unsigned char)*pch][0];
1815 *(pt + 2) = HexList[(unsigned char)*pch][1];
1817 OutBuf->BufUsed += 3;
1825 * @ingroup StrBuf_DeEnCoder
1826 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1827 * @param OutBuf the output buffer
1828 * @param In Buffer to encode
1829 * @param PlainIn way in from plain old c strings
1831 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1833 const char *pch, *pche;
1837 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1839 if (PlainIn != NULL) {
1840 len = strlen(PlainIn);
1846 pche = pch + In->BufUsed;
1853 pt = OutBuf->buf + OutBuf->BufUsed;
1854 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1856 while (pch < pche) {
1858 IncreaseBuf(OutBuf, 1, -1);
1859 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1860 pt = OutBuf->buf + OutBuf->BufUsed;
1863 if((*pch >= 'a' && *pch <= 'z') ||
1864 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1865 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1866 (*pch == '!') || (*pch == '_') ||
1867 (*pch == ',') || (*pch == '.'))
1874 *(pt + 1) = HexList[(unsigned char)*pch][0];
1875 *(pt + 2) = HexList[(unsigned char)*pch][1];
1877 OutBuf->BufUsed += 3;
1885 * @ingroup StrBuf_DeEnCoder
1886 * @brief append a string in hex encoding to the buffer
1887 * @param OutBuf the output buffer
1888 * @param In Buffer to encode
1889 * @param PlainIn way in from plain old c strings
1890 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1892 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1894 const unsigned char *pch, *pche;
1898 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1900 if (PlainIn != NULL) {
1902 len = strlen((const char*)PlainIn);
1909 pch = (const unsigned char*)In->buf;
1910 pche = pch + In->BufUsed;
1917 pt = OutBuf->buf + OutBuf->BufUsed;
1918 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1920 while (pch < pche) {
1922 IncreaseBuf(OutBuf, 1, -1);
1923 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1924 pt = OutBuf->buf + OutBuf->BufUsed;
1927 *pt = HexList[*pch][0];
1929 *pt = HexList[*pch][1];
1930 pt ++; pch ++; OutBuf->BufUsed += 2;
1936 * @ingroup StrBuf_DeEnCoder
1937 * @brief append a string in hex encoding to the buffer
1938 * @param OutBuf the output buffer
1939 * @param In Buffer to encode
1940 * @param PlainIn way in from plain old c strings
1942 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1944 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1948 * @ingroup StrBuf_DeEnCoder
1949 * @brief Append a string, escaping characters which have meaning in HTML.
1951 * @param Target target buffer
1952 * @param Source source buffer; set to NULL if you just have a C-String
1953 * @param PlainIn Plain-C string to append; set to NULL if unused
1954 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1955 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1956 * if set to 2, linebreaks are replaced by <br/>
1958 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1960 const char *aptr, *eiptr;
1964 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1967 if (PlainIn != NULL) {
1969 len = strlen(PlainIn);
1974 eiptr = aptr + Source->BufUsed;
1975 len = Source->BufUsed;
1981 bptr = Target->buf + Target->BufUsed;
1982 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1984 while (aptr < eiptr){
1986 IncreaseBuf(Target, 1, -1);
1987 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1988 bptr = Target->buf + Target->BufUsed;
1991 memcpy(bptr, "<", 4);
1993 Target->BufUsed += 4;
1995 else if (*aptr == '>') {
1996 memcpy(bptr, ">", 4);
1998 Target->BufUsed += 4;
2000 else if (*aptr == '&') {
2001 memcpy(bptr, "&", 5);
2003 Target->BufUsed += 5;
2005 else if (*aptr == '"') {
2006 memcpy(bptr, """, 6);
2008 Target->BufUsed += 6;
2010 else if (*aptr == '\'') {
2011 memcpy(bptr, "'", 5);
2013 Target->BufUsed += 5;
2015 else if (*aptr == LB) {
2020 else if (*aptr == RB) {
2025 else if (*aptr == QU) {
2030 else if ((*aptr == 32) && (nbsp == 1)) {
2031 memcpy(bptr, " ", 6);
2033 Target->BufUsed += 6;
2035 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2036 *bptr='\0'; /* nothing */
2038 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2039 memcpy(bptr, "<br/>", 11);
2041 Target->BufUsed += 11;
2045 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2046 *bptr='\0'; /* nothing */
2056 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2058 return Target->BufUsed;
2062 * @ingroup StrBuf_DeEnCoder
2063 * @brief Append a string, escaping characters which have meaning in HTML.
2064 * Converts linebreaks into blanks; escapes single quotes
2065 * @param Target target buffer
2066 * @param Source source buffer; set to NULL if you just have a C-String
2067 * @param PlainIn Plain-C string to append; set to NULL if unused
2069 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2071 const char *aptr, *eiptr;
2075 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2078 if (PlainIn != NULL) {
2080 len = strlen(PlainIn);
2085 eiptr = aptr + Source->BufUsed;
2086 len = Source->BufUsed;
2092 eptr = Target->buf + Target->BufSize - 8;
2093 tptr = Target->buf + Target->BufUsed;
2095 while (aptr < eiptr){
2097 IncreaseBuf(Target, 1, -1);
2098 eptr = Target->buf + Target->BufSize - 8;
2099 tptr = Target->buf + Target->BufUsed;
2102 if (*aptr == '\n') {
2106 else if (*aptr == '\r') {
2110 else if (*aptr == '\'') {
2116 Target->BufUsed += 5;
2129 * @ingroup StrBuf_DeEnCoder
2130 * @brief Append a string, escaping characters which have meaning in ICAL.
2132 * @param Target target buffer
2133 * @param Source source buffer; set to NULL if you just have a C-String
2134 * @param PlainIn Plain-C string to append; set to NULL if unused
2136 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2138 const char *aptr, *eiptr;
2142 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2145 if (PlainIn != NULL) {
2147 len = strlen(PlainIn);
2152 eiptr = aptr + Source->BufUsed;
2153 len = Source->BufUsed;
2159 eptr = Target->buf + Target->BufSize - 8;
2160 tptr = Target->buf + Target->BufUsed;
2162 while (aptr < eiptr){
2163 if(tptr + 3 >= eptr) {
2164 IncreaseBuf(Target, 1, -1);
2165 eptr = Target->buf + Target->BufSize - 8;
2166 tptr = Target->buf + Target->BufUsed;
2169 if (*aptr == '\n') {
2176 else if (*aptr == '\r') {
2183 else if (*aptr == ',') {
2199 * @ingroup StrBuf_DeEnCoder
2200 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2202 * @param Target target buffer
2203 * @param Source source buffer; set to NULL if you just have a C-String
2204 * @param PlainIn Plain-C string to append; set to NULL if unused
2205 * @returns size of result or -1
2207 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2209 const char *aptr, *eiptr;
2214 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2217 if (PlainIn != NULL) {
2219 len = strlen(PlainIn);
2224 eiptr = aptr + Source->BufUsed;
2225 len = Source->BufUsed;
2231 bptr = Target->buf + Target->BufUsed;
2232 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2234 while (aptr < eiptr){
2236 IncreaseBuf(Target, 1, -1);
2237 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2238 bptr = Target->buf + Target->BufUsed;
2242 memcpy(bptr, HKEY("\\n"));
2244 Target->BufUsed += 2;
2247 memcpy(bptr, HKEY("\\r"));
2249 Target->BufUsed += 2;
2256 Target->BufUsed += 2;
2259 if ((*(aptr + 1) == 'u') &&
2260 isxdigit(*(aptr + 2)) &&
2261 isxdigit(*(aptr + 3)) &&
2262 isxdigit(*(aptr + 4)) &&
2263 isxdigit(*(aptr + 5)))
2264 { /* oh, a unicode escaper. let it pass through. */
2265 memcpy(bptr, aptr, 6);
2268 Target->BufUsed += 6;
2276 Target->BufUsed += 2;
2284 Target->BufUsed += 2;
2291 Target->BufUsed += 2;
2298 Target->BufUsed += 2;
2301 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2302 while (IsUtf8Sequence > 0){
2305 if (--IsUtf8Sequence)
2313 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2315 return Target->BufUsed;
2319 * @ingroup StrBuf_DeEnCoder
2320 * @brief Append a string, escaping characters which have meaning in HTML + json.
2322 * @param Target target buffer
2323 * @param Source source buffer; set to NULL if you just have a C-String
2324 * @param PlainIn Plain-C string to append; set to NULL if unused
2325 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2326 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2327 * if set to 2, linebreaks are replaced by <br/>
2329 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2331 const char *aptr, *eiptr;
2334 int IsUtf8Sequence = 0;
2336 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2339 if (PlainIn != NULL) {
2341 len = strlen(PlainIn);
2346 eiptr = aptr + Source->BufUsed;
2347 len = Source->BufUsed;
2353 bptr = Target->buf + Target->BufUsed;
2354 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2356 while (aptr < eiptr){
2358 IncreaseBuf(Target, 1, -1);
2359 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2360 bptr = Target->buf + Target->BufUsed;
2364 memcpy(bptr, HKEY("<"));
2366 Target->BufUsed += 4;
2369 memcpy(bptr, HKEY(">"));
2371 Target->BufUsed += 4;
2374 memcpy(bptr, HKEY("&"));
2376 Target->BufUsed += 5;
2389 switch (nolinebreaks) {
2391 *bptr='\0'; /* nothing */
2394 memcpy(bptr, HKEY("<br/>"));
2396 Target->BufUsed += 11;
2399 memcpy(bptr, HKEY("\\n"));
2401 Target->BufUsed += 2;
2405 switch (nolinebreaks) {
2408 *bptr='\0'; /* nothing */
2411 memcpy(bptr, HKEY("\\r"));
2413 Target->BufUsed += 2;
2423 Target->BufUsed += 2;
2426 if ((*(aptr + 1) == 'u') &&
2427 isxdigit(*(aptr + 2)) &&
2428 isxdigit(*(aptr + 3)) &&
2429 isxdigit(*(aptr + 4)) &&
2430 isxdigit(*(aptr + 5)))
2431 { /* oh, a unicode escaper. let it pass through. */
2432 memcpy(bptr, aptr, 6);
2435 Target->BufUsed += 6;
2443 Target->BufUsed += 2;
2451 Target->BufUsed += 2;
2458 Target->BufUsed += 2;
2465 Target->BufUsed += 2;
2469 memcpy(bptr, HKEY(" "));
2471 Target->BufUsed += 6;
2475 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2476 while (IsUtf8Sequence > 0){
2479 if (--IsUtf8Sequence)
2487 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2489 return Target->BufUsed;
2493 * @ingroup StrBuf_DeEnCoder
2494 * @brief unhide special chars hidden to the HTML escaper
2495 * @param target buffer to put the unescaped string in
2496 * @param source buffer to unescape
2498 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2504 FlushStrBuf(target);
2506 if (source == NULL ||target == NULL)
2511 len = source->BufUsed;
2512 for (a = 0; a < len; ++a) {
2513 if (target->BufUsed >= target->BufSize)
2514 IncreaseBuf(target, 1, -1);
2516 if (source->buf[a] == '=') {
2517 hex[0] = source->buf[a + 1];
2518 hex[1] = source->buf[a + 2];
2521 sscanf(hex, "%02x", &b);
2522 target->buf[target->BufUsed] = b;
2523 target->buf[++target->BufUsed] = 0;
2527 target->buf[target->BufUsed] = source->buf[a];
2528 target->buf[++target->BufUsed] = 0;
2535 * @ingroup StrBuf_DeEnCoder
2536 * @brief hide special chars from the HTML escapers and friends
2537 * @param target buffer to put the escaped string in
2538 * @param source buffer to escape
2540 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2545 FlushStrBuf(target);
2547 if (source == NULL ||target == NULL)
2552 len = source->BufUsed;
2553 for (i=0; i<len; ++i) {
2554 if (target->BufUsed + 4 >= target->BufSize)
2555 IncreaseBuf(target, 1, -1);
2556 if ( (isalnum(source->buf[i])) ||
2557 (source->buf[i]=='-') ||
2558 (source->buf[i]=='_') ) {
2559 target->buf[target->BufUsed++] = source->buf[i];
2562 sprintf(&target->buf[target->BufUsed],
2564 (0xFF &source->buf[i]));
2565 target->BufUsed += 3;
2568 target->buf[target->BufUsed + 1] = '\0';
2572 /*******************************************************************************
2573 * Quoted Printable de/encoding *
2574 *******************************************************************************/
2577 * @ingroup StrBuf_DeEnCoder
2578 * @brief decode a buffer from base 64 encoding; destroys original
2579 * @param Buf Buffor to transform
2581 int StrBufDecodeBase64(StrBuf *Buf)
2585 if (Buf == NULL) return -1;
2587 xferbuf = (char*) malloc(Buf->BufSize);
2589 siz = CtdlDecodeBase64(xferbuf,
2599 * @ingroup StrBuf_DeEnCoder
2600 * @brief decode a buffer from base 64 encoding; destroys original
2601 * @param Buf Buffor to transform
2603 int StrBufDecodeHex(StrBuf *Buf)
2606 char *pch, *pche, *pchi;
2608 if (Buf == NULL) return -1;
2610 pch = pchi = Buf->buf;
2611 pche = pch + Buf->BufUsed;
2613 while (pchi < pche){
2614 ch = decode_hex(pchi);
2621 Buf->BufUsed = pch - Buf->buf;
2622 return Buf->BufUsed;
2626 * @ingroup StrBuf_DeEnCoder
2627 * @brief replace all chars >0x20 && < 0x7F with Mute
2628 * @param Mute char to put over invalid chars
2629 * @param Buf Buffor to transform
2631 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2635 if (Buf == NULL) return -1;
2636 pch = (unsigned char *)Buf->buf;
2637 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2638 if ((*pch < 0x20) || (*pch > 0x7F))
2642 return Buf->BufUsed;
2647 * @ingroup StrBuf_DeEnCoder
2648 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2649 * @param Buf Buffer to translate
2650 * @param StripBlanks Reduce several blanks to one?
2652 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2661 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2662 Buf->buf[Buf->BufUsed - 1] = '\0';
2667 while (a < Buf->BufUsed) {
2668 if (Buf->buf[a] == '+')
2670 else if (Buf->buf[a] == '%') {
2671 /* don't let % chars through, rather truncate the input. */
2672 if (a + 2 > Buf->BufUsed) {
2677 hex[0] = Buf->buf[a + 1];
2678 hex[1] = Buf->buf[a + 2];
2681 sscanf(hex, "%02x", &b);
2682 Buf->buf[a] = (char) b;
2683 len = Buf->BufUsed - a - 2;
2685 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2697 * @ingroup StrBuf_DeEnCoder
2698 * @brief RFC2047-encode a header field if necessary.
2699 * If no non-ASCII characters are found, the string
2700 * will be copied verbatim without encoding.
2702 * @param target Target buffer.
2703 * @param source Source string to be encoded.
2704 * @returns encoded length; -1 if non success.
2706 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2708 const char headerStr[] = "=?UTF-8?Q?";
2709 int need_to_encode = 0;
2713 if ((source == NULL) ||
2717 while ((i < source->BufUsed) &&
2718 (!IsEmptyStr (&source->buf[i])) &&
2719 (need_to_encode == 0)) {
2720 if (((unsigned char) source->buf[i] < 32) ||
2721 ((unsigned char) source->buf[i] > 126)) {
2727 if (!need_to_encode) {
2728 if (*target == NULL) {
2729 *target = NewStrBufPlain(source->buf, source->BufUsed);
2732 FlushStrBuf(*target);
2733 StrBufAppendBuf(*target, source, 0);
2735 return (*target)->BufUsed;
2737 if (*target == NULL)
2738 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2739 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2740 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2741 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2742 (*target)->BufUsed = sizeof(headerStr) - 1;
2743 for (i=0; (i < source->BufUsed); ++i) {
2744 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2745 IncreaseBuf(*target, 1, 0);
2746 ch = (unsigned char) source->buf[i];
2756 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2757 (*target)->BufUsed += 3;
2761 (*target)->buf[(*target)->BufUsed] = '_';
2763 (*target)->buf[(*target)->BufUsed] = ch;
2764 (*target)->BufUsed++;
2768 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2769 IncreaseBuf(*target, 1, 0);
2771 (*target)->buf[(*target)->BufUsed++] = '?';
2772 (*target)->buf[(*target)->BufUsed++] = '=';
2773 (*target)->buf[(*target)->BufUsed] = '\0';
2774 return (*target)->BufUsed;;
2779 static void AddRecipient(StrBuf *Target,
2781 StrBuf *EmailAddress,
2786 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2787 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2789 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2790 StrBufRFC2047encode(&EncBuf, UserName);
2791 StrBufAppendBuf(Target, EncBuf, 0);
2792 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2793 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2795 if (StrLength(EmailAddress) > 0){
2796 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2797 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2798 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2804 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2805 * \param Recp Source list of email recipients
2806 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2807 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2808 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2809 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2811 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2813 StrBuf *EmailAddress,
2817 const char *pch, *pche;
2818 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2820 if ((Recp == NULL) || (StrLength(Recp) == 0))
2824 pche = pch + StrLength(Recp);
2826 if (!CheckEncode(pch, -1, pche))
2827 return NewStrBufDup(Recp);
2829 Target = NewStrBufPlain(NULL, StrLength(Recp));
2831 while ((pch != NULL) && (pch < pche))
2833 while (isspace(*pch)) pch++;
2834 UserEnd = EmailStart = EmailEnd = NULL;
2836 if ((*pch == '"') || (*pch == '\'')) {
2837 UserStart = pch + 1;
2839 UserEnd = strchr(UserStart, *pch);
2840 if (UserEnd == NULL)
2841 break; ///TODO: Userfeedback??
2842 EmailStart = UserEnd + 1;
2843 while (isspace(*EmailStart))
2845 if (UserEnd == UserStart) {
2846 UserStart = UserEnd = NULL;
2849 if (*EmailStart == '<') {
2851 EmailEnd = strchr(EmailStart, '>');
2852 if (EmailEnd == NULL)
2853 EmailEnd = strchr(EmailStart, ',');
2857 EmailEnd = strchr(EmailStart, ',');
2859 if (EmailEnd == NULL)
2866 EmailEnd = strchr(UserStart, ',');
2867 if (EmailEnd == NULL) {
2868 EmailEnd = strchr(pch, '>');
2870 if (EmailEnd != NULL) {
2880 while ((EmailEnd > UserStart) && !gt &&
2881 ((*EmailEnd == ',') ||
2882 (*EmailEnd == '>') ||
2883 (isspace(*EmailEnd))))
2885 if (*EmailEnd == '>')
2890 if (EmailEnd == UserStart)
2894 EmailStart = strchr(UserStart, '<');
2895 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2897 UserEnd = EmailStart;
2899 while ((UserEnd > UserStart) &&
2900 isspace (*(UserEnd - 1)))
2903 if (UserStart >= UserEnd)
2904 UserStart = UserEnd = NULL;
2906 else { /* this is a local recipient... no domain, just a realname */
2907 EmailStart = UserStart;
2908 At = strchr(EmailStart, '@');
2914 EmailStart = UserStart;
2920 if ((UserStart != NULL) && (UserEnd != NULL))
2921 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2922 else if ((UserStart != NULL) && (UserEnd == NULL))
2923 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2925 FlushStrBuf(UserName);
2927 if ((EmailStart != NULL) && (EmailEnd != NULL))
2928 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2929 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2930 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2932 FlushStrBuf(EmailAddress);
2934 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2939 if ((pch != NULL) && (*pch == ','))
2941 if (pch != NULL) while (isspace(*pch))
2950 * @brief replaces all occurances of 'search' by 'replace'
2951 * @param buf Buffer to modify
2952 * @param search character to search
2953 * @param replace character to replace search by
2955 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2960 for (i=0; i<buf->BufUsed; i++)
2961 if (buf->buf[i] == search)
2962 buf->buf[i] = replace;
2968 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2969 * @param buf Buffer to modify
2971 void StrBufToUnixLF(StrBuf *buf)
2973 char *pche, *pchS, *pchT;
2977 pche = buf->buf + buf->BufUsed;
2978 pchS = pchT = buf->buf;
2984 if (*pchS != '\n') {
2993 buf->BufUsed = pchT - buf->buf;
2997 /*******************************************************************************
2998 * Iconv Wrapper; RFC822 de/encoding *
2999 *******************************************************************************/
3002 * @ingroup StrBuf_DeEnCoder
3003 * @brief Wrapper around iconv_open()
3004 * Our version adds aliases for non-standard Microsoft charsets
3005 * such as 'MS950', aliasing them to names like 'CP950'
3007 * @param tocode Target encoding
3008 * @param fromcode Source encoding
3009 * @param pic anonimized pointer to iconv struct
3011 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3014 iconv_t ic = (iconv_t)(-1) ;
3015 ic = iconv_open(tocode, fromcode);
3016 if (ic == (iconv_t)(-1) ) {
3017 char alias_fromcode[64];
3018 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3019 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3020 alias_fromcode[0] = 'C';
3021 alias_fromcode[1] = 'P';
3022 ic = iconv_open(tocode, alias_fromcode);
3025 *(iconv_t *)pic = ic;
3031 * @ingroup StrBuf_DeEnCoder
3032 * @brief find one chunk of a RFC822 encoded string
3033 * @param Buffer where to search
3034 * @param bptr where to start searching
3035 * @returns found position, NULL if none.
3037 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3040 /* Find the next ?Q? */
3041 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3044 end = strchr(bptr + 2, '?');
3049 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3050 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3051 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3052 (*(end + 2) == '?')) {
3053 /* skip on to the end of the cluster, the next ?= */
3054 end = strstr(end + 3, "?=");
3057 /* sort of half valid encoding, try to find an end. */
3058 end = strstr(bptr, "?=");
3065 * @ingroup StrBuf_DeEnCoder
3066 * @brief convert one buffer according to the preselected iconv pointer PIC
3067 * @param ConvertBuf buffer we need to translate
3068 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3069 * @param pic Pointer to the iconv-session Object
3071 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3077 char *ibuf; /**< Buffer of characters to be converted */
3078 char *obuf; /**< Buffer for converted characters */
3079 size_t ibuflen; /**< Length of input buffer */
3080 size_t obuflen; /**< Length of output buffer */
3083 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3084 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3085 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3087 ic = *(iconv_t*)pic;
3088 ibuf = ConvertBuf->buf;
3089 ibuflen = ConvertBuf->BufUsed;
3091 obuflen = TmpBuf->BufSize;
3093 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3096 if (errno == E2BIG) {
3098 IncreaseBuf(TmpBuf, 0, 0);
3103 else if (errno == EILSEQ){
3104 /* hm, invalid utf8 sequence... what to do now? */
3105 /* An invalid multibyte sequence has been encountered in the input */
3107 else if (errno == EINVAL) {
3108 /* An incomplete multibyte sequence has been encountered in the input. */
3111 FlushStrBuf(TmpBuf);
3114 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3115 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3117 /* little card game: wheres the red lady? */
3118 SwapBuffers(ConvertBuf, TmpBuf);
3119 FlushStrBuf(TmpBuf);
3126 * @ingroup StrBuf_DeEnCoder
3127 * @brief catches one RFC822 encoded segment, and decodes it.
3128 * @param Target buffer to fill with result
3129 * @param DecodeMe buffer with stuff to process
3130 * @param SegmentStart points to our current segment in DecodeMe
3131 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3132 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3133 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3134 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3136 inline static void DecodeSegment(StrBuf *Target,
3137 const StrBuf *DecodeMe,
3138 const char *SegmentStart,
3139 const char *SegmentEnd,
3141 StrBuf *ConvertBuf2,
3142 StrBuf *FoundCharset)
3148 iconv_t ic = (iconv_t)(-1);
3152 /* Now we handle foreign character sets properly encoded
3153 * in RFC2047 format.
3155 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3156 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3157 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3158 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3159 if (FoundCharset != NULL) {
3160 FlushStrBuf(FoundCharset);
3161 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3163 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3164 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3166 *encoding = toupper(*encoding);
3167 if (*encoding == 'B') { /**< base64 */
3168 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3169 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3170 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3172 ConvertBuf->BufUsed);
3174 else if (*encoding == 'Q') { /**< quoted-printable */
3178 while (pos < ConvertBuf->BufUsed)
3180 if (ConvertBuf->buf[pos] == '_')
3181 ConvertBuf->buf[pos] = ' ';
3185 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3186 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3188 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3191 ConvertBuf->BufUsed);
3194 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3197 ctdl_iconv_open("UTF-8", charset, &ic);
3198 if (ic != (iconv_t)(-1) ) {
3200 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3201 StrBufAppendBuf(Target, ConvertBuf2, 0);
3206 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3212 * @ingroup StrBuf_DeEnCoder
3213 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3214 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3215 * @param Target where to put the decoded string to
3216 * @param DecodeMe buffer with encoded string
3217 * @param DefaultCharset if we don't find one, which should we use?
3218 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3219 * put it here for later use where no string might be known.
3221 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3224 StrBuf *ConvertBuf2;
3225 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3226 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3228 StrBuf_RFC822_2_Utf8(Target,
3234 FreeStrBuf(&ConvertBuf);
3235 FreeStrBuf(&ConvertBuf2);
3239 * @ingroup StrBuf_DeEnCoder
3240 * @brief Handle subjects with RFC2047 encoding such as:
3241 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3242 * @param Target where to put the decoded string to
3243 * @param DecodeMe buffer with encoded string
3244 * @param DefaultCharset if we don't find one, which should we use?
3245 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3246 * put it here for later use where no string might be known.
3247 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3248 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3250 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3251 const StrBuf *DecodeMe,
3252 const StrBuf* DefaultCharset,
3253 StrBuf *FoundCharset,
3255 StrBuf *ConvertBuf2)
3257 StrBuf *DecodedInvalidBuf = NULL;
3258 const StrBuf *DecodeMee = DecodeMe;
3259 const char *start, *end, *next, *nextend, *ptr = NULL;
3261 iconv_t ic = (iconv_t)(-1) ;
3266 int illegal_non_rfc2047_encoding = 0;
3268 /* Sometimes, badly formed messages contain strings which were simply
3269 * written out directly in some foreign character set instead of
3270 * using RFC2047 encoding. This is illegal but we will attempt to
3271 * handle it anyway by converting from a user-specified default
3272 * charset to UTF-8 if we see any nonprintable characters.
3275 for (i=0; i<DecodeMe->BufUsed; ++i) {
3276 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3277 illegal_non_rfc2047_encoding = 1;
3282 if ((illegal_non_rfc2047_encoding) &&
3283 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3284 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3287 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3288 if (ic != (iconv_t)(-1) ) {
3289 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3290 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3291 DecodeMee = DecodedInvalidBuf;
3297 /* pre evaluate the first pair */
3299 start = strstr(DecodeMee->buf, "=?");
3300 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3302 end = FindNextEnd (DecodeMee, start + 2);
3304 StrBufAppendBuf(Target, DecodeMee, 0);
3305 FreeStrBuf(&DecodedInvalidBuf);
3310 if (start != DecodeMee->buf) {
3313 nFront = start - DecodeMee->buf;
3314 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3317 * Since spammers will go to all sorts of absurd lengths to get their
3318 * messages through, there are LOTS of corrupt headers out there.
3319 * So, prevent a really badly formed RFC2047 header from throwing
3320 * this function into an infinite loop.
3322 while ((start != NULL) &&
3329 DecodeSegment(Target,
3337 next = strstr(end, "=?");
3339 if ((next != NULL) &&
3341 nextend = FindNextEnd(DecodeMee, next);
3342 if (nextend == NULL)
3345 /* did we find two partitions */
3346 if ((next != NULL) &&
3350 while ((ptr < next) &&
3357 * did we find a gab just filled with blanks?
3358 * if not, copy its stuff over.
3362 StrBufAppendBufPlain(Target,
3368 /* our next-pair is our new first pair now. */
3374 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3375 if ((end != NULL) && (end < nextend)) {
3377 while ( (ptr < nextend) &&
3384 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3386 FreeStrBuf(&DecodedInvalidBuf);
3389 /*******************************************************************************
3390 * Manipulating UTF-8 Strings *
3391 *******************************************************************************/
3395 * @brief evaluate the length of an utf8 special character sequence
3396 * @param Char the character to examine
3397 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3399 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3402 unsigned char test = (1<<7);
3404 if ((*CharS & 0xC0) != 0xC0)
3408 ((test & ((unsigned char)*CharS)) != 0))
3413 if ((n > 6) || ((CharE - CharS) < n))
3420 * @brief detect whether this char starts an utf-8 encoded char
3421 * @param Char character to inspect
3422 * @returns yes or no
3424 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3426 /** 11??.???? indicates an UTF8 Sequence. */
3427 return ((Char & 0xC0) == 0xC0);
3432 * @brief measure the number of glyphs in an UTF8 string...
3433 * @param Buf string to measure
3434 * @returns the number of glyphs in Buf
3436 long StrBuf_Utf8StrLen(StrBuf *Buf)
3442 if ((Buf == NULL) || (Buf->BufUsed == 0))
3445 eptr = Buf->buf + Buf->BufUsed;
3446 while ((aptr < eptr) && (*aptr != '\0')) {
3447 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3448 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3449 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3462 * @brief cuts a string after maxlen glyphs
3463 * @param Buf string to cut to maxlen glyphs
3464 * @param maxlen how long may the string become?
3465 * @returns current length of the string
3467 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3473 eptr = Buf->buf + Buf->BufUsed;
3474 while ((aptr < eptr) && (*aptr != '\0')) {
3475 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3476 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3477 while ((*aptr++ != '\0') && (m-- > 0));
3486 Buf->BufUsed = aptr - Buf->buf;
3487 return Buf->BufUsed;
3490 return Buf->BufUsed;
3498 /*******************************************************************************
3500 *******************************************************************************/
3503 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3504 #define OS_CODE 0x03 /*< unix */
3507 * @ingroup StrBuf_DeEnCoder
3508 * @brief uses the same calling syntax as compress2(), but it
3509 * creates a stream compatible with HTTP "Content-encoding: gzip"
3510 * @param dest compressed buffer
3511 * @param destLen length of the compresed data
3512 * @param source source to encode
3513 * @param sourceLen length of source to encode
3514 * @param level compression level
3516 int ZEXPORT compress_gzip(Bytef * dest,
3518 const Bytef * source,
3522 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3524 /* write gzip header */
3525 snprintf((char *) dest, *destLen,
3526 "%c%c%c%c%c%c%c%c%c%c",
3527 gz_magic[0], gz_magic[1], Z_DEFLATED,
3528 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3531 /* normal deflate */
3534 stream.next_in = (Bytef *) source;
3535 stream.avail_in = (uInt) sourceLen;
3536 stream.next_out = dest + 10L; // after header
3537 stream.avail_out = (uInt) * destLen;
3538 if ((uLong) stream.avail_out != *destLen)
3541 stream.zalloc = (alloc_func) 0;
3542 stream.zfree = (free_func) 0;
3543 stream.opaque = (voidpf) 0;
3545 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3546 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3550 err = deflate(&stream, Z_FINISH);
3551 if (err != Z_STREAM_END) {
3552 deflateEnd(&stream);
3553 return err == Z_OK ? Z_BUF_ERROR : err;
3555 *destLen = stream.total_out + 10L;
3557 /* write CRC and Length */
3558 uLong crc = crc32(0L, source, sourceLen);
3560 for (n = 0; n < 4; ++n, ++*destLen) {
3561 dest[*destLen] = (int) (crc & 0xff);
3564 uLong len = stream.total_in;
3565 for (n = 0; n < 4; ++n, ++*destLen) {
3566 dest[*destLen] = (int) (len & 0xff);
3569 err = deflateEnd(&stream);
3576 * @ingroup StrBuf_DeEnCoder
3577 * @brief compress the buffer with gzip
3578 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3579 * @param Buf buffer whose content is to be gzipped
3581 int CompressBuffer(StrBuf *Buf)
3584 char *compressed_data = NULL;
3585 size_t compressed_len, bufsize;
3588 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3589 compressed_data = malloc(compressed_len);
3591 if (compressed_data == NULL)
3593 /* Flush some space after the used payload so valgrind shuts up... */
3594 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3595 Buf->buf[Buf->BufUsed + i++] = '\0';
3596 if (compress_gzip((Bytef *) compressed_data,
3599 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3602 Buf->buf = compressed_data;
3603 Buf->BufUsed = compressed_len;
3604 Buf->BufSize = bufsize;
3605 /* Flush some space after the used payload so valgrind shuts up... */
3607 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3608 Buf->buf[Buf->BufUsed + i++] = '\0';
3611 free(compressed_data);
3613 #endif /* HAVE_ZLIB */
3617 /*******************************************************************************
3618 * File I/O; Callbacks to libevent *
3619 *******************************************************************************/
3621 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3626 if ((FB == NULL) || (FB->Buf == NULL))
3630 * check whether the read pointer is somewhere in a range
3631 * where a cut left is inexpensive
3634 if (FB->ReadWritePointer != NULL)
3638 already_read = FB->ReadWritePointer - FB->Buf->buf;
3639 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3641 if (already_read != 0) {
3644 unread = FB->Buf->BufUsed - already_read;
3646 /* else nothing to compact... */
3648 FB->ReadWritePointer = FB->Buf->buf;
3649 bufremain = FB->Buf->BufSize;
3651 else if ((unread < 64) ||
3652 (bufremain < already_read))
3655 * if its just a tiny bit remaining, or we run out of space...
3658 FB->Buf->BufUsed = unread;
3659 if (unread < already_read)
3660 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3662 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3663 FB->ReadWritePointer = FB->Buf->buf;
3664 bufremain = FB->Buf->BufSize - unread - 1;
3666 else if (bufremain < (FB->Buf->BufSize / 10))
3668 /* get a bigger buffer */
3670 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3672 FB->ReadWritePointer = FB->Buf->buf + unread;
3674 bufremain = FB->Buf->BufSize - unread - 1;
3675 /*TODO: special increase function that won't copy the already read! */
3678 else if (bufremain < 10) {
3679 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3681 FB->ReadWritePointer = FB->Buf->buf;
3683 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3688 FB->ReadWritePointer = FB->Buf->buf;
3689 bufremain = FB->Buf->BufSize - 1;
3692 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3695 FB->Buf->BufUsed += n;
3696 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3701 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3706 if ((FB == NULL) || (FB->Buf == NULL))
3709 if (FB->ReadWritePointer != NULL)
3711 WriteRemain = FB->Buf->BufUsed -
3712 (FB->ReadWritePointer -
3716 FB->ReadWritePointer = FB->Buf->buf;
3717 WriteRemain = FB->Buf->BufUsed;
3720 n = write(fd, FB->ReadWritePointer, WriteRemain);
3722 FB->ReadWritePointer += n;
3724 if (FB->ReadWritePointer ==
3725 FB->Buf->buf + FB->Buf->BufUsed)
3727 FlushStrBuf(FB->Buf);
3728 FB->ReadWritePointer = NULL;
3731 // check whether we've got something to write
3732 // get the maximum chunk plus the pointer we can send
3733 // write whats there
3734 // if not all was sent, remember the send pointer for the next time
3735 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3741 * @ingroup StrBuf_IO
3742 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3743 * @param LineBuf your line will be copied here.
3744 * @param FB BLOB with lines of text...
3745 * @param Ptr moved arround to keep the next-line across several iterations
3746 * has to be &NULL on start; will be &NotNULL on end of buffer
3747 * @returns size of copied buffer
3749 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3751 const char *aptr, *ptr, *eptr;
3754 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3755 FB->ReadWritePointer = StrBufNOTNULL;
3759 FlushStrBuf(LineBuf);
3760 if (FB->ReadWritePointer == NULL)
3761 ptr = aptr = FB->Buf->buf;
3763 ptr = aptr = FB->ReadWritePointer;
3765 optr = LineBuf->buf;
3766 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3767 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3769 while ((ptr <= eptr) &&
3776 LineBuf->BufUsed = optr - LineBuf->buf;
3777 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3778 optr = LineBuf->buf + LineBuf->BufUsed;
3779 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3784 if (optr > LineBuf->buf)
3786 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3787 LineBuf->BufUsed = optr - LineBuf->buf;
3789 if ((FB->ReadWritePointer != NULL) &&
3790 (FB->ReadWritePointer != FB->Buf->buf))
3792 /* Ok, the client application read all the data
3793 it was interested in so far. Since there is more to read,
3794 we now shrink the buffer, and move the rest over.
3796 StrBufCutLeft(FB->Buf,
3797 FB->ReadWritePointer - FB->Buf->buf);
3798 FB->ReadWritePointer = FB->Buf->buf;
3800 return eMustReadMore;
3803 LineBuf->BufUsed = optr - LineBuf->buf;
3805 if ((ptr <= eptr) && (*ptr == '\r'))
3807 if ((ptr <= eptr) && (*ptr == '\n'))
3811 FB->ReadWritePointer = ptr;
3814 FlushStrBuf(FB->Buf);
3815 FB->ReadWritePointer = NULL;
3818 return eReadSuccess;
3822 * @ingroup StrBuf_CHUNKED_IO
3823 * @brief check whether the chunk-buffer has more data waiting or not.
3824 * @param FB Chunk-Buffer to inspect
3826 eReadState StrBufCheckBuffer(IOBuffer *FB)
3830 if (FB->Buf->BufUsed == 0)
3831 return eReadSuccess;
3832 if (FB->ReadWritePointer == NULL)
3833 return eBufferNotEmpty;
3834 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3835 return eBufferNotEmpty;
3836 return eReadSuccess;
3839 long IOBufferStrLength(IOBuffer *FB)
3841 if (FB->ReadWritePointer == NULL)
3842 return StrLength(FB->Buf);
3844 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3847 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3849 memset(FDB, 0, sizeof(FDIOBuffer));
3851 FDB->TotalSendSize = TotalSendSize;
3853 #ifndef LINUX_SENDFILE
3854 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3856 pipe(FDB->SplicePipe);
3861 void FDIOBufferDelete(FDIOBuffer *FDB)
3863 #ifndef LINUX_SENDFILE
3864 FreeStrBuf(&FDB->ChunkBuffer);
3866 close(FDB->SplicePipe[0]);
3867 close(FDB->SplicePipe[1]);
3870 close(FDB->OtherFD);
3871 memset(FDB, 0, sizeof(FDIOBuffer));
3874 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3877 #ifdef LINUX_SENDFILE
3879 sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3882 *Err = strerror(errno);
3885 FDB->ChunkSendRemain -= sent;
3886 FDB->TotalSentAlready += sent;
3887 return FDB->ChunkSendRemain;
3893 pRead = FDB->ChunkBuffer->buf;
3894 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3896 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3898 FDB->ChunkBuffer->BufUsed += nRead;
3899 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3901 else if (nRead == 0) {}
3906 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3909 FDB->TotalSentAlready += nRead;
3910 FDB->ChunkSendRemain -= nRead;
3911 return FDB->ChunkSendRemain;
3919 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3921 ssize_t sent, pipesize;
3923 #ifdef LINUX_SENDFILE
3925 pipesize = splice(FDB->IOB->fd, NULL,
3926 FDB->SplicePipe[1], NULL,
3927 FDB->ChunkSendRemain,
3928 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3931 *Err = strerror(errno);
3935 sent = splice(FDB->SplicePipe[0], NULL,
3936 FDB->OtherFD, &FDB->TotalSentAlready,
3937 pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
3940 *Err = strerror(errno);
3943 FDB->ChunkSendRemain -= sent;
3947 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
3952 FDB->ChunkBuffer->BufUsed = sent;
3954 while (nWritten < FDB->ChunkBuffer->BufUsed) {
3955 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
3957 *Err = strerror(errno);
3963 FDB->ChunkBuffer->BufUsed = 0;
3964 FDB->TotalSentAlready += sent;
3965 FDB->ChunkSendRemain -= sent;
3966 return FDB->ChunkSendRemain;
3968 else if (sent < 0) {
3969 *Err = strerror(errno);
3977 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
3983 int nSuccessLess = 0;
3987 fdflags = fcntl(FDB->OtherFD, F_GETFL);
3988 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3990 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
3991 (FDB->ChunkSendRemain > 0))
3994 tv.tv_sec = 1; /* selectresolution; */
3998 FD_SET(FDB->OtherFD, &rfds);
3999 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4000 *Error = strerror(errno);
4004 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4009 should_write = FDB->IOB->Buf->BufUsed -
4010 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4011 if (should_write > FDB->ChunkSendRemain)
4012 should_write = FDB->ChunkSendRemain;
4014 rlen = write(FDB->OtherFD,
4015 FDB->IOB->ReadWritePointer,
4018 *Error = strerror(errno);
4022 FDB->TotalSentAlready += rlen;
4023 FDB->IOB->ReadWritePointer += rlen;
4024 FDB->ChunkSendRemain -= rlen;
4026 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4028 FlushStrBuf(FDB->IOB->Buf);
4029 FDB->IOB->ReadWritePointer = NULL;
4032 if (FDB->ChunkSendRemain == 0)
4033 return eReadSuccess;
4035 return eMustReadMore;
4038 /*******************************************************************************
4039 * File I/O; Prefer buffered read since its faster! *
4040 *******************************************************************************/
4043 * @ingroup StrBuf_IO
4044 * @brief Read a line from socket
4045 * flushes and closes the FD on error
4046 * @param buf the buffer to get the input to
4047 * @param fd pointer to the filedescriptor to read
4048 * @param append Append to an existing string or replace?
4049 * @param Error strerror() on error
4050 * @returns numbers of chars read
4052 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4054 int len, rlen, slen;
4059 slen = len = buf->BufUsed;
4061 rlen = read(*fd, &buf->buf[len], 1);
4063 *Error = strerror(errno);
4070 if (buf->buf[len] == '\n')
4072 if (buf->buf[len] != '\r')
4074 if (len + 2 >= buf->BufSize) {
4076 buf->buf[len+1] = '\0';
4077 IncreaseBuf(buf, 1, -1);
4081 buf->buf[len] = '\0';
4086 * @ingroup StrBuf_BufferedIO
4087 * @brief Read a line from socket
4088 * flushes and closes the FD on error
4089 * @param Line the line to read from the fd / I/O Buffer
4090 * @param buf the buffer to get the input to
4091 * @param fd pointer to the filedescriptor to read
4092 * @param timeout number of successless selects until we bail out
4093 * @param selectresolution how long to wait on each select
4094 * @param Error strerror() on error
4095 * @returns numbers of chars read
4097 int StrBufTCP_read_buffered_line(StrBuf *Line,
4101 int selectresolution,
4105 int nSuccessLess = 0;
4112 if (buf->BufUsed > 0) {
4113 pch = strchr(buf->buf, '\n');
4116 len = pch - buf->buf;
4117 if (len > 0 && (*(pch - 1) == '\r') )
4119 StrBufSub(Line, buf, 0, len - rlen);
4120 StrBufCutLeft(buf, len + 1);
4125 if (buf->BufSize - buf->BufUsed < 10)
4126 IncreaseBuf(buf, 1, -1);
4128 fdflags = fcntl(*fd, F_GETFL);
4129 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4131 while ((nSuccessLess < timeout) && (pch == NULL)) {
4133 tv.tv_sec = selectresolution;
4138 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4139 *Error = strerror(errno);
4145 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4150 &buf->buf[buf->BufUsed],
4151 buf->BufSize - buf->BufUsed - 1);
4153 *Error = strerror(errno);
4158 else if (rlen > 0) {
4160 buf->BufUsed += rlen;
4161 buf->buf[buf->BufUsed] = '\0';
4162 if (buf->BufUsed + 10 > buf->BufSize) {
4163 IncreaseBuf(buf, 1, -1);
4165 pch = strchr(buf->buf, '\n');
4172 len = pch - buf->buf;
4173 if (len > 0 && (*(pch - 1) == '\r') )
4175 StrBufSub(Line, buf, 0, len - rlen);
4176 StrBufCutLeft(buf, len + 1);
4183 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4184 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4185 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4187 * @ingroup StrBuf_BufferedIO
4188 * @brief Read a line from socket
4189 * flushes and closes the FD on error
4190 * @param Line where to append our Line read from the fd / I/O Buffer;
4191 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4192 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4193 * @param fd pointer to the filedescriptor to read
4194 * @param timeout number of successless selects until we bail out
4195 * @param selectresolution how long to wait on each select
4196 * @param Error strerror() on error
4197 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4199 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4204 int selectresolution,
4207 const char *pche = NULL;
4208 const char *pos = NULL;
4210 int len, rlen, retlen;
4211 int nSuccessLess = 0;
4213 const char *pch = NULL;
4219 if ((Line == NULL) ||
4226 *Error = ErrRBLF_PreConditionFailed;
4231 if ((IOBuf->BufUsed > 0) &&
4233 (pos < IOBuf->buf + IOBuf->BufUsed))
4237 pche = IOBuf->buf + IOBuf->BufUsed;
4241 while ((pch < pche) && (*pch != '\n'))
4243 if (Line->BufUsed + 10 > Line->BufSize)
4246 apos = pcht - Line->buf;
4248 IncreaseBuf(Line, 1, -1);
4249 pcht = Line->buf + apos;
4257 if (len > 0 && (*(pch - 1) == '\r') )
4266 if ((pch >= pche) || (*pch == '\0'))
4274 if ((pch != NULL) &&
4277 if (pch + 1 >= pche) {
4290 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4292 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4293 IncreaseBuf(IOBuf, 1, -1);
4295 fdflags = fcntl(*fd, F_GETFL);
4296 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4299 while ((nSuccessLess < timeout) &&
4309 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4310 *Error = strerror(errno);
4314 *Error = ErrRBLF_SelectFailed;
4317 if (! FD_ISSET(*fd, &rfds) != 0) {
4323 &IOBuf->buf[IOBuf->BufUsed],
4324 IOBuf->BufSize - IOBuf->BufUsed - 1);
4326 *Error = strerror(errno);
4331 else if (rlen > 0) {
4333 pLF = IOBuf->buf + IOBuf->BufUsed;
4334 IOBuf->BufUsed += rlen;
4335 IOBuf->buf[IOBuf->BufUsed] = '\0';
4337 pche = IOBuf->buf + IOBuf->BufUsed;
4339 while ((pLF < pche) && (*pLF != '\n'))
4341 if ((pLF >= pche) || (*pLF == '\0'))
4344 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4348 if (pLF != NULL) apos = pLF - IOBuf->buf;
4349 IncreaseBuf(IOBuf, 1, -1);
4350 if (pLF != NULL) pLF = IOBuf->buf + apos;
4360 if (len > 0 && (*(pLF - 1) == '\r') )
4362 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4363 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4369 return retlen + len;
4371 *Error = ErrRBLF_NotEnoughSentFromServer;
4376 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4378 * @ingroup StrBuf_IO
4379 * @brief Input binary data from socket
4380 * flushes and closes the FD on error
4381 * @param Buf the buffer to get the input to
4382 * @param fd pointer to the filedescriptor to read
4383 * @param append Append to an existing string or replace?
4384 * @param nBytes the maximal number of bytes to read
4385 * @param Error strerror() on error
4386 * @returns numbers of chars read
4388 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4399 if ((Buf == NULL) || (*fd == -1))
4401 *Error = ErrRBLF_BLOBPreConditionFailed;
4406 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4407 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4409 ptr = Buf->buf + Buf->BufUsed;
4411 fdflags = fcntl(*fd, F_GETFL);
4412 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4414 while ((nRead < nBytes) &&
4424 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4425 *Error = strerror(errno);
4429 *Error = ErrRBLF_SelectFailed;
4432 if (! FD_ISSET(*fd, &rfds) != 0) {
4438 if ((rlen = read(*fd,
4440 nBytes - nRead)) == -1) {
4443 *Error = strerror(errno);
4448 Buf->BufUsed += rlen;
4450 Buf->buf[Buf->BufUsed] = '\0';
4454 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4455 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4457 * @ingroup StrBuf_BufferedIO
4458 * @brief Input binary data from socket
4459 * flushes and closes the FD on error
4460 * @param Blob put binary thing here
4461 * @param IOBuf the buffer to get the input to
4462 * @param Pos offset inside of IOBuf
4463 * @param fd pointer to the filedescriptor to read
4464 * @param append Append to an existing string or replace?
4465 * @param nBytes the maximal number of bytes to read
4466 * @param check whether we should search for '000\n' terminators in case of timeouts
4467 * @param Error strerror() on error
4468 * @returns numbers of chars read
4470 int StrBufReadBLOBBuffered(StrBuf *Blob,
4483 int nAlreadyRead = 0;
4488 int nSuccessLess = 0;
4491 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4495 *Error = ErrRBB_BLOBFPreConditionFailed;
4501 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4502 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4507 rlen = pos - IOBuf->buf;
4508 rlen = IOBuf->BufUsed - rlen;
4511 if ((IOBuf->BufUsed > 0) &&
4513 (pos < IOBuf->buf + IOBuf->BufUsed))
4515 if (rlen < nBytes) {
4516 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4517 Blob->BufUsed += rlen;
4518 Blob->buf[Blob->BufUsed] = '\0';
4519 nAlreadyRead = nRead = rlen;
4522 if (rlen >= nBytes) {
4523 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4524 Blob->BufUsed += nBytes;
4525 Blob->buf[Blob->BufUsed] = '\0';
4526 if (rlen == nBytes) {
4538 if (IOBuf->BufSize < nBytes - nRead)
4539 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4542 fdflags = fcntl(*fd, F_GETFL);
4543 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4551 while ((nSuccessLess < MaxTries) &&
4561 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4562 *Error = strerror(errno);
4566 *Error = ErrRBLF_SelectFailed;
4569 if (! FD_ISSET(*fd, &rfds) != 0) {
4576 IOBuf->BufSize - (ptr - IOBuf->buf));
4580 *Error = strerror(errno);
4583 else if (rlen == 0){
4584 if ((check == NNN_TERM) &&
4586 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4588 StrBufPlain(Blob, HKEY("\n000\n"));
4589 StrBufCutRight(Blob, 5);
4590 return Blob->BufUsed;
4592 else if (!IsNonBlock)
4594 else if (nSuccessLess > MaxTries) {
4596 *Error = ErrRBB_too_many_selects;
4600 else if (rlen > 0) {
4604 IOBuf->BufUsed += rlen;
4607 if (nSuccessLess >= MaxTries) {
4609 *Error = ErrRBB_too_many_selects;
4613 if (nRead > nBytes) {
4614 *Pos = IOBuf->buf + nBytes;
4616 Blob->buf[Blob->BufUsed] = '\0';
4617 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4621 return nRead + nAlreadyRead;
4625 * @ingroup StrBuf_IO
4626 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4627 * @param LineBuf your line will be copied here.
4628 * @param Buf BLOB with lines of text...
4629 * @param Ptr moved arround to keep the next-line across several iterations
4630 * has to be &NULL on start; will be &NotNULL on end of buffer
4631 * @returns size of remaining buffer
4633 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4635 const char *aptr, *ptr, *eptr;
4638 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4639 *Ptr = StrBufNOTNULL;
4643 FlushStrBuf(LineBuf);
4645 ptr = aptr = Buf->buf;
4649 optr = LineBuf->buf;
4650 eptr = Buf->buf + Buf->BufUsed;
4651 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4653 while ((ptr <= eptr) &&
4660 LineBuf->BufUsed = optr - LineBuf->buf;
4661 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4662 optr = LineBuf->buf + LineBuf->BufUsed;
4663 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4667 if ((ptr >= eptr) && (optr > LineBuf->buf))
4669 LineBuf->BufUsed = optr - LineBuf->buf;
4671 if ((ptr <= eptr) && (*ptr == '\r'))
4673 if ((ptr <= eptr) && (*ptr == '\n'))
4680 *Ptr = StrBufNOTNULL;
4683 return Buf->BufUsed - (ptr - Buf->buf);
4688 * @ingroup StrBuf_IO
4689 * @brief removes double slashes from pathnames
4690 * @param Dir directory string to filter
4691 * @param RemoveTrailingSlash allows / disallows trailing slashes
4693 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4699 while (!IsEmptyStr(a)) {
4711 if ((RemoveTrailingSlash) &&
4717 Dir->BufUsed = b - Dir->buf;