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)
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 UserStart = 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;
2905 At = strchr(EmailStart, '@');
2907 else { /* this is a local recipient... no domain, just a realname */
2908 EmailStart = UserStart;
2909 At = strchr(EmailStart, '@');
2915 EmailStart = UserStart;
2921 if ((UserStart != NULL) && (UserEnd != NULL))
2922 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2923 else if ((UserStart != NULL) && (UserEnd == NULL))
2924 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2926 FlushStrBuf(UserName);
2928 if ((EmailStart != NULL) && (EmailEnd != NULL))
2929 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2930 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2931 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2933 FlushStrBuf(EmailAddress);
2935 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2940 if ((pch != NULL) && (*pch == ','))
2942 if (pch != NULL) while (isspace(*pch))
2951 * @brief replaces all occurances of 'search' by 'replace'
2952 * @param buf Buffer to modify
2953 * @param search character to search
2954 * @param replace character to replace search by
2956 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2961 for (i=0; i<buf->BufUsed; i++)
2962 if (buf->buf[i] == search)
2963 buf->buf[i] = replace;
2969 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2970 * @param buf Buffer to modify
2972 void StrBufToUnixLF(StrBuf *buf)
2974 char *pche, *pchS, *pchT;
2978 pche = buf->buf + buf->BufUsed;
2979 pchS = pchT = buf->buf;
2985 if (*pchS != '\n') {
2994 buf->BufUsed = pchT - buf->buf;
2998 /*******************************************************************************
2999 * Iconv Wrapper; RFC822 de/encoding *
3000 *******************************************************************************/
3003 * @ingroup StrBuf_DeEnCoder
3004 * @brief Wrapper around iconv_open()
3005 * Our version adds aliases for non-standard Microsoft charsets
3006 * such as 'MS950', aliasing them to names like 'CP950'
3008 * @param tocode Target encoding
3009 * @param fromcode Source encoding
3010 * @param pic anonimized pointer to iconv struct
3012 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3015 iconv_t ic = (iconv_t)(-1) ;
3016 ic = iconv_open(tocode, fromcode);
3017 if (ic == (iconv_t)(-1) ) {
3018 char alias_fromcode[64];
3019 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3020 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3021 alias_fromcode[0] = 'C';
3022 alias_fromcode[1] = 'P';
3023 ic = iconv_open(tocode, alias_fromcode);
3026 *(iconv_t *)pic = ic;
3032 * @ingroup StrBuf_DeEnCoder
3033 * @brief find one chunk of a RFC822 encoded string
3034 * @param Buffer where to search
3035 * @param bptr where to start searching
3036 * @returns found position, NULL if none.
3038 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3041 /* Find the next ?Q? */
3042 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3045 end = strchr(bptr + 2, '?');
3050 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3051 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3052 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3053 (*(end + 2) == '?')) {
3054 /* skip on to the end of the cluster, the next ?= */
3055 end = strstr(end + 3, "?=");
3058 /* sort of half valid encoding, try to find an end. */
3059 end = strstr(bptr, "?=");
3066 * @ingroup StrBuf_DeEnCoder
3067 * @brief convert one buffer according to the preselected iconv pointer PIC
3068 * @param ConvertBuf buffer we need to translate
3069 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3070 * @param pic Pointer to the iconv-session Object
3072 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3078 char *ibuf; /**< Buffer of characters to be converted */
3079 char *obuf; /**< Buffer for converted characters */
3080 size_t ibuflen; /**< Length of input buffer */
3081 size_t obuflen; /**< Length of output buffer */
3084 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3085 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3086 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3088 ic = *(iconv_t*)pic;
3089 ibuf = ConvertBuf->buf;
3090 ibuflen = ConvertBuf->BufUsed;
3092 obuflen = TmpBuf->BufSize;
3094 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3097 if (errno == E2BIG) {
3099 IncreaseBuf(TmpBuf, 0, 0);
3104 else if (errno == EILSEQ){
3105 /* hm, invalid utf8 sequence... what to do now? */
3106 /* An invalid multibyte sequence has been encountered in the input */
3108 else if (errno == EINVAL) {
3109 /* An incomplete multibyte sequence has been encountered in the input. */
3112 FlushStrBuf(TmpBuf);
3115 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3116 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3118 /* little card game: wheres the red lady? */
3119 SwapBuffers(ConvertBuf, TmpBuf);
3120 FlushStrBuf(TmpBuf);
3127 * @ingroup StrBuf_DeEnCoder
3128 * @brief catches one RFC822 encoded segment, and decodes it.
3129 * @param Target buffer to fill with result
3130 * @param DecodeMe buffer with stuff to process
3131 * @param SegmentStart points to our current segment in DecodeMe
3132 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3133 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3134 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3135 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3137 inline static void DecodeSegment(StrBuf *Target,
3138 const StrBuf *DecodeMe,
3139 const char *SegmentStart,
3140 const char *SegmentEnd,
3142 StrBuf *ConvertBuf2,
3143 StrBuf *FoundCharset)
3149 iconv_t ic = (iconv_t)(-1);
3153 /* Now we handle foreign character sets properly encoded
3154 * in RFC2047 format.
3156 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3157 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3158 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3159 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3160 if (FoundCharset != NULL) {
3161 FlushStrBuf(FoundCharset);
3162 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3164 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3165 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3167 *encoding = toupper(*encoding);
3168 if (*encoding == 'B') { /**< base64 */
3169 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3170 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3171 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3173 ConvertBuf->BufUsed);
3175 else if (*encoding == 'Q') { /**< quoted-printable */
3179 while (pos < ConvertBuf->BufUsed)
3181 if (ConvertBuf->buf[pos] == '_')
3182 ConvertBuf->buf[pos] = ' ';
3186 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3187 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3189 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3192 ConvertBuf->BufUsed);
3195 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3198 ctdl_iconv_open("UTF-8", charset, &ic);
3199 if (ic != (iconv_t)(-1) ) {
3201 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3202 StrBufAppendBuf(Target, ConvertBuf2, 0);
3207 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3213 * @ingroup StrBuf_DeEnCoder
3214 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3215 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3216 * @param Target where to put the decoded string to
3217 * @param DecodeMe buffer with encoded string
3218 * @param DefaultCharset if we don't find one, which should we use?
3219 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3220 * put it here for later use where no string might be known.
3222 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3225 StrBuf *ConvertBuf2;
3226 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3227 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3229 StrBuf_RFC822_2_Utf8(Target,
3235 FreeStrBuf(&ConvertBuf);
3236 FreeStrBuf(&ConvertBuf2);
3240 * @ingroup StrBuf_DeEnCoder
3241 * @brief Handle subjects with RFC2047 encoding such as:
3242 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3243 * @param Target where to put the decoded string to
3244 * @param DecodeMe buffer with encoded string
3245 * @param DefaultCharset if we don't find one, which should we use?
3246 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3247 * put it here for later use where no string might be known.
3248 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3249 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3251 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3252 const StrBuf *DecodeMe,
3253 const StrBuf* DefaultCharset,
3254 StrBuf *FoundCharset,
3256 StrBuf *ConvertBuf2)
3258 StrBuf *DecodedInvalidBuf = NULL;
3259 const StrBuf *DecodeMee = DecodeMe;
3260 const char *start, *end, *next, *nextend, *ptr = NULL;
3262 iconv_t ic = (iconv_t)(-1) ;
3267 int illegal_non_rfc2047_encoding = 0;
3269 /* Sometimes, badly formed messages contain strings which were simply
3270 * written out directly in some foreign character set instead of
3271 * using RFC2047 encoding. This is illegal but we will attempt to
3272 * handle it anyway by converting from a user-specified default
3273 * charset to UTF-8 if we see any nonprintable characters.
3276 len = StrLength(DecodeMe);
3277 for (i=0; i<DecodeMe->BufUsed; ++i) {
3278 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3279 illegal_non_rfc2047_encoding = 1;
3284 if ((illegal_non_rfc2047_encoding) &&
3285 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3286 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3289 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3290 if (ic != (iconv_t)(-1) ) {
3291 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3292 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3293 DecodeMee = DecodedInvalidBuf;
3299 /* pre evaluate the first pair */
3300 nextend = end = NULL;
3301 len = StrLength(DecodeMee);
3302 start = strstr(DecodeMee->buf, "=?");
3303 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3305 end = FindNextEnd (DecodeMee, start + 2);
3307 StrBufAppendBuf(Target, DecodeMee, 0);
3308 FreeStrBuf(&DecodedInvalidBuf);
3313 if (start != DecodeMee->buf) {
3316 nFront = start - DecodeMee->buf;
3317 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3321 * Since spammers will go to all sorts of absurd lengths to get their
3322 * messages through, there are LOTS of corrupt headers out there.
3323 * So, prevent a really badly formed RFC2047 header from throwing
3324 * this function into an infinite loop.
3326 while ((start != NULL) &&
3333 DecodeSegment(Target,
3341 next = strstr(end, "=?");
3343 if ((next != NULL) &&
3345 nextend = FindNextEnd(DecodeMee, next);
3346 if (nextend == NULL)
3349 /* did we find two partitions */
3350 if ((next != NULL) &&
3354 while ((ptr < next) &&
3361 * did we find a gab just filled with blanks?
3362 * if not, copy its stuff over.
3366 StrBufAppendBufPlain(Target,
3372 /* our next-pair is our new first pair now. */
3378 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3379 if ((end != NULL) && (end < nextend)) {
3381 while ( (ptr < nextend) &&
3388 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3390 FreeStrBuf(&DecodedInvalidBuf);
3393 /*******************************************************************************
3394 * Manipulating UTF-8 Strings *
3395 *******************************************************************************/
3399 * @brief evaluate the length of an utf8 special character sequence
3400 * @param Char the character to examine
3401 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3403 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3406 unsigned char test = (1<<7);
3408 if ((*CharS & 0xC0) != 0xC0)
3412 ((test & ((unsigned char)*CharS)) != 0))
3417 if ((n > 6) || ((CharE - CharS) < n))
3424 * @brief detect whether this char starts an utf-8 encoded char
3425 * @param Char character to inspect
3426 * @returns yes or no
3428 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3430 /** 11??.???? indicates an UTF8 Sequence. */
3431 return ((Char & 0xC0) == 0xC0);
3436 * @brief measure the number of glyphs in an UTF8 string...
3437 * @param Buf string to measure
3438 * @returns the number of glyphs in Buf
3440 long StrBuf_Utf8StrLen(StrBuf *Buf)
3446 if ((Buf == NULL) || (Buf->BufUsed == 0))
3449 eptr = Buf->buf + Buf->BufUsed;
3450 while ((aptr < eptr) && (*aptr != '\0')) {
3451 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3452 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3453 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3466 * @brief cuts a string after maxlen glyphs
3467 * @param Buf string to cut to maxlen glyphs
3468 * @param maxlen how long may the string become?
3469 * @returns current length of the string
3471 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3477 eptr = Buf->buf + Buf->BufUsed;
3478 while ((aptr < eptr) && (*aptr != '\0')) {
3479 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3480 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3481 while ((*aptr++ != '\0') && (m-- > 0));
3490 Buf->BufUsed = aptr - Buf->buf;
3491 return Buf->BufUsed;
3494 return Buf->BufUsed;
3502 /*******************************************************************************
3504 *******************************************************************************/
3507 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3508 #define OS_CODE 0x03 /*< unix */
3511 * @ingroup StrBuf_DeEnCoder
3512 * @brief uses the same calling syntax as compress2(), but it
3513 * creates a stream compatible with HTTP "Content-encoding: gzip"
3514 * @param dest compressed buffer
3515 * @param destLen length of the compresed data
3516 * @param source source to encode
3517 * @param sourceLen length of source to encode
3518 * @param level compression level
3520 int ZEXPORT compress_gzip(Bytef * dest,
3522 const Bytef * source,
3526 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3528 /* write gzip header */
3529 snprintf((char *) dest, *destLen,
3530 "%c%c%c%c%c%c%c%c%c%c",
3531 gz_magic[0], gz_magic[1], Z_DEFLATED,
3532 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3535 /* normal deflate */
3538 stream.next_in = (Bytef *) source;
3539 stream.avail_in = (uInt) sourceLen;
3540 stream.next_out = dest + 10L; // after header
3541 stream.avail_out = (uInt) * destLen;
3542 if ((uLong) stream.avail_out != *destLen)
3545 stream.zalloc = (alloc_func) 0;
3546 stream.zfree = (free_func) 0;
3547 stream.opaque = (voidpf) 0;
3549 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3550 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3554 err = deflate(&stream, Z_FINISH);
3555 if (err != Z_STREAM_END) {
3556 deflateEnd(&stream);
3557 return err == Z_OK ? Z_BUF_ERROR : err;
3559 *destLen = stream.total_out + 10L;
3561 /* write CRC and Length */
3562 uLong crc = crc32(0L, source, sourceLen);
3564 for (n = 0; n < 4; ++n, ++*destLen) {
3565 dest[*destLen] = (int) (crc & 0xff);
3568 uLong len = stream.total_in;
3569 for (n = 0; n < 4; ++n, ++*destLen) {
3570 dest[*destLen] = (int) (len & 0xff);
3573 err = deflateEnd(&stream);
3580 * @ingroup StrBuf_DeEnCoder
3581 * @brief compress the buffer with gzip
3582 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3583 * @param Buf buffer whose content is to be gzipped
3585 int CompressBuffer(StrBuf *Buf)
3588 char *compressed_data = NULL;
3589 size_t compressed_len, bufsize;
3592 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3593 compressed_data = malloc(compressed_len);
3595 if (compressed_data == NULL)
3597 /* Flush some space after the used payload so valgrind shuts up... */
3598 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3599 Buf->buf[Buf->BufUsed + i++] = '\0';
3600 if (compress_gzip((Bytef *) compressed_data,
3603 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3606 Buf->buf = compressed_data;
3607 Buf->BufUsed = compressed_len;
3608 Buf->BufSize = bufsize;
3609 /* Flush some space after the used payload so valgrind shuts up... */
3611 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3612 Buf->buf[Buf->BufUsed + i++] = '\0';
3615 free(compressed_data);
3617 #endif /* HAVE_ZLIB */
3621 /*******************************************************************************
3622 * File I/O; Callbacks to libevent *
3623 *******************************************************************************/
3625 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3630 if ((FB == NULL) || (FB->Buf == NULL))
3634 * check whether the read pointer is somewhere in a range
3635 * where a cut left is inexpensive
3638 if (FB->ReadWritePointer != NULL)
3642 already_read = FB->ReadWritePointer - FB->Buf->buf;
3643 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3645 if (already_read != 0) {
3648 unread = FB->Buf->BufUsed - already_read;
3650 /* else nothing to compact... */
3652 FB->ReadWritePointer = FB->Buf->buf;
3653 bufremain = FB->Buf->BufSize;
3655 else if ((unread < 64) ||
3656 (bufremain < already_read))
3659 * if its just a tiny bit remaining, or we run out of space...
3662 FB->Buf->BufUsed = unread;
3663 if (unread < already_read)
3664 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3666 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3667 FB->ReadWritePointer = FB->Buf->buf;
3668 bufremain = FB->Buf->BufSize - unread - 1;
3670 else if (bufremain < (FB->Buf->BufSize / 10))
3672 /* get a bigger buffer */
3674 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3676 FB->ReadWritePointer = FB->Buf->buf + unread;
3678 bufremain = FB->Buf->BufSize - unread - 1;
3679 /*TODO: special increase function that won't copy the already read! */
3682 else if (bufremain < 10) {
3683 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3685 FB->ReadWritePointer = FB->Buf->buf;
3687 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3692 FB->ReadWritePointer = FB->Buf->buf;
3693 bufremain = FB->Buf->BufSize - 1;
3696 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3699 FB->Buf->BufUsed += n;
3700 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3705 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3710 if ((FB == NULL) || (FB->Buf == NULL))
3713 if (FB->ReadWritePointer != NULL)
3715 WriteRemain = FB->Buf->BufUsed -
3716 (FB->ReadWritePointer -
3720 FB->ReadWritePointer = FB->Buf->buf;
3721 WriteRemain = FB->Buf->BufUsed;
3724 n = write(fd, FB->ReadWritePointer, WriteRemain);
3726 FB->ReadWritePointer += n;
3728 if (FB->ReadWritePointer ==
3729 FB->Buf->buf + FB->Buf->BufUsed)
3731 FlushStrBuf(FB->Buf);
3732 FB->ReadWritePointer = NULL;
3735 // check whether we've got something to write
3736 // get the maximum chunk plus the pointer we can send
3737 // write whats there
3738 // if not all was sent, remember the send pointer for the next time
3739 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3745 * @ingroup StrBuf_IO
3746 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3747 * @param LineBuf your line will be copied here.
3748 * @param FB BLOB with lines of text...
3749 * @param Ptr moved arround to keep the next-line across several iterations
3750 * has to be &NULL on start; will be &NotNULL on end of buffer
3751 * @returns size of copied buffer
3753 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3755 const char *aptr, *ptr, *eptr;
3758 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3759 FB->ReadWritePointer = StrBufNOTNULL;
3763 FlushStrBuf(LineBuf);
3764 if (FB->ReadWritePointer == NULL)
3765 ptr = aptr = FB->Buf->buf;
3767 ptr = aptr = FB->ReadWritePointer;
3769 optr = LineBuf->buf;
3770 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3771 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3773 while ((ptr <= eptr) &&
3780 LineBuf->BufUsed = optr - LineBuf->buf;
3781 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3782 optr = LineBuf->buf + LineBuf->BufUsed;
3783 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3788 if (optr > LineBuf->buf)
3790 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3791 LineBuf->BufUsed = optr - LineBuf->buf;
3793 if ((FB->ReadWritePointer != NULL) &&
3794 (FB->ReadWritePointer != FB->Buf->buf))
3796 /* Ok, the client application read all the data
3797 it was interested in so far. Since there is more to read,
3798 we now shrink the buffer, and move the rest over.
3800 StrBufCutLeft(FB->Buf,
3801 FB->ReadWritePointer - FB->Buf->buf);
3802 FB->ReadWritePointer = FB->Buf->buf;
3804 return eMustReadMore;
3807 LineBuf->BufUsed = optr - LineBuf->buf;
3809 if ((ptr <= eptr) && (*ptr == '\r'))
3811 if ((ptr <= eptr) && (*ptr == '\n'))
3815 FB->ReadWritePointer = ptr;
3818 FlushStrBuf(FB->Buf);
3819 FB->ReadWritePointer = NULL;
3822 return eReadSuccess;
3826 * @ingroup StrBuf_CHUNKED_IO
3827 * @brief check whether the chunk-buffer has more data waiting or not.
3828 * @param FB Chunk-Buffer to inspect
3830 eReadState StrBufCheckBuffer(IOBuffer *FB)
3834 if (FB->Buf->BufUsed == 0)
3835 return eReadSuccess;
3836 if (FB->ReadWritePointer == NULL)
3837 return eBufferNotEmpty;
3838 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3839 return eBufferNotEmpty;
3840 return eReadSuccess;
3843 long IOBufferStrLength(IOBuffer *FB)
3845 if (FB->ReadWritePointer == NULL)
3846 return StrLength(FB->Buf);
3848 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3851 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3853 memset(FDB, 0, sizeof(FDIOBuffer));
3855 FDB->TotalSendSize = TotalSendSize;
3857 #ifndef LINUX_SENDFILE
3858 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3860 pipe(FDB->SplicePipe);
3865 void FDIOBufferDelete(FDIOBuffer *FDB)
3867 #ifndef LINUX_SENDFILE
3868 FreeStrBuf(&FDB->ChunkBuffer);
3870 close(FDB->SplicePipe[0]);
3871 close(FDB->SplicePipe[1]);
3874 close(FDB->OtherFD);
3875 memset(FDB, 0, sizeof(FDIOBuffer));
3878 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3883 #ifdef LINUX_SENDFILE
3885 sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3888 *Err = strerror(errno);
3891 FDB->ChunkSendRemain -= sent;
3892 FDB->TotalSentAlready += sent;
3893 return FDB->ChunkSendRemain;
3895 pRead = FDB->ChunkBuffer->buf;
3896 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3898 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3900 FDB->ChunkBuffer->BufUsed += nRead;
3901 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3903 else if (nRead == 0) {}
3908 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3911 FDB->TotalSentAlready += nRead;
3912 FDB->ChunkSendRemain -= nRead;
3913 return FDB->ChunkSendRemain;
3921 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3923 ssize_t sent, pipesize;
3925 #ifdef LINUX_SENDFILE
3927 pipesize = splice(FDB->IOB->fd, NULL,
3928 FDB->SplicePipe[1], NULL,
3929 FDB->ChunkSendRemain,
3930 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3933 *Err = strerror(errno);
3937 sent = splice(FDB->SplicePipe[0], NULL,
3938 FDB->OtherFD, &FDB->TotalSentAlready,
3939 pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
3942 *Err = strerror(errno);
3945 FDB->ChunkSendRemain -= sent;
3949 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
3954 FDB->ChunkBuffer->BufUsed = sent;
3956 while (nWritten < FDB->ChunkBuffer->BufUsed) {
3957 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
3959 *Err = strerror(errno);
3965 FDB->ChunkBuffer->BufUsed = 0;
3966 FDB->TotalSentAlready += sent;
3967 FDB->ChunkSendRemain -= sent;
3968 return FDB->ChunkSendRemain;
3970 else if (sent < 0) {
3971 *Err = strerror(errno);
3979 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
3985 int nSuccessLess = 0;
3989 fdflags = fcntl(FDB->OtherFD, F_GETFL);
3990 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3992 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
3993 (FDB->ChunkSendRemain > 0))
3996 tv.tv_sec = 1; /* selectresolution; */
4000 FD_SET(FDB->OtherFD, &rfds);
4001 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4002 *Error = strerror(errno);
4006 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4011 should_write = FDB->IOB->Buf->BufUsed -
4012 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4013 if (should_write > FDB->ChunkSendRemain)
4014 should_write = FDB->ChunkSendRemain;
4016 rlen = write(FDB->OtherFD,
4017 FDB->IOB->ReadWritePointer,
4020 *Error = strerror(errno);
4024 FDB->TotalSentAlready += rlen;
4025 FDB->IOB->ReadWritePointer += rlen;
4026 FDB->ChunkSendRemain -= rlen;
4028 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4030 FlushStrBuf(FDB->IOB->Buf);
4031 FDB->IOB->ReadWritePointer = NULL;
4034 if (FDB->ChunkSendRemain == 0)
4035 return eReadSuccess;
4037 return eMustReadMore;
4040 /*******************************************************************************
4041 * File I/O; Prefer buffered read since its faster! *
4042 *******************************************************************************/
4045 * @ingroup StrBuf_IO
4046 * @brief Read a line from socket
4047 * flushes and closes the FD on error
4048 * @param buf the buffer to get the input to
4049 * @param fd pointer to the filedescriptor to read
4050 * @param append Append to an existing string or replace?
4051 * @param Error strerror() on error
4052 * @returns numbers of chars read
4054 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4056 int len, rlen, slen;
4061 slen = len = buf->BufUsed;
4063 rlen = read(*fd, &buf->buf[len], 1);
4065 *Error = strerror(errno);
4072 if (buf->buf[len] == '\n')
4074 if (buf->buf[len] != '\r')
4076 if (len + 2 >= buf->BufSize) {
4078 buf->buf[len+1] = '\0';
4079 IncreaseBuf(buf, 1, -1);
4083 buf->buf[len] = '\0';
4088 * @ingroup StrBuf_BufferedIO
4089 * @brief Read a line from socket
4090 * flushes and closes the FD on error
4091 * @param Line the line to read from the fd / I/O Buffer
4092 * @param buf the buffer to get the input to
4093 * @param fd pointer to the filedescriptor to read
4094 * @param timeout number of successless selects until we bail out
4095 * @param selectresolution how long to wait on each select
4096 * @param Error strerror() on error
4097 * @returns numbers of chars read
4099 int StrBufTCP_read_buffered_line(StrBuf *Line,
4103 int selectresolution,
4107 int nSuccessLess = 0;
4114 if (buf->BufUsed > 0) {
4115 pch = strchr(buf->buf, '\n');
4118 len = pch - buf->buf;
4119 if (len > 0 && (*(pch - 1) == '\r') )
4121 StrBufSub(Line, buf, 0, len - rlen);
4122 StrBufCutLeft(buf, len + 1);
4127 if (buf->BufSize - buf->BufUsed < 10)
4128 IncreaseBuf(buf, 1, -1);
4130 fdflags = fcntl(*fd, F_GETFL);
4131 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4133 while ((nSuccessLess < timeout) && (pch == NULL)) {
4135 tv.tv_sec = selectresolution;
4140 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4141 *Error = strerror(errno);
4147 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4152 &buf->buf[buf->BufUsed],
4153 buf->BufSize - buf->BufUsed - 1);
4155 *Error = strerror(errno);
4160 else if (rlen > 0) {
4162 buf->BufUsed += rlen;
4163 buf->buf[buf->BufUsed] = '\0';
4164 if (buf->BufUsed + 10 > buf->BufSize) {
4165 IncreaseBuf(buf, 1, -1);
4167 pch = strchr(buf->buf, '\n');
4174 len = pch - buf->buf;
4175 if (len > 0 && (*(pch - 1) == '\r') )
4177 StrBufSub(Line, buf, 0, len - rlen);
4178 StrBufCutLeft(buf, len + 1);
4185 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4186 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4187 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4189 * @ingroup StrBuf_BufferedIO
4190 * @brief Read a line from socket
4191 * flushes and closes the FD on error
4192 * @param Line where to append our Line read from the fd / I/O Buffer;
4193 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4194 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4195 * @param fd pointer to the filedescriptor to read
4196 * @param timeout number of successless selects until we bail out
4197 * @param selectresolution how long to wait on each select
4198 * @param Error strerror() on error
4199 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4201 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4206 int selectresolution,
4209 const char *pche = NULL;
4210 const char *pos = NULL;
4212 int len, rlen, retlen;
4213 int nSuccessLess = 0;
4215 const char *pch = NULL;
4221 if ((Line == NULL) ||
4228 *Error = ErrRBLF_PreConditionFailed;
4233 if ((IOBuf->BufUsed > 0) &&
4235 (pos < IOBuf->buf + IOBuf->BufUsed))
4239 pche = IOBuf->buf + IOBuf->BufUsed;
4243 while ((pch < pche) && (*pch != '\n'))
4245 if (Line->BufUsed + 10 > Line->BufSize)
4248 apos = pcht - Line->buf;
4250 IncreaseBuf(Line, 1, -1);
4251 pcht = Line->buf + apos;
4259 if (len > 0 && (*(pch - 1) == '\r') )
4268 if ((pch >= pche) || (*pch == '\0'))
4276 if ((pch != NULL) &&
4279 if (pch + 1 >= pche) {
4292 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4294 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4295 IncreaseBuf(IOBuf, 1, -1);
4297 fdflags = fcntl(*fd, F_GETFL);
4298 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4301 while ((nSuccessLess < timeout) &&
4311 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4312 *Error = strerror(errno);
4316 *Error = ErrRBLF_SelectFailed;
4319 if (! FD_ISSET(*fd, &rfds) != 0) {
4325 &IOBuf->buf[IOBuf->BufUsed],
4326 IOBuf->BufSize - IOBuf->BufUsed - 1);
4328 *Error = strerror(errno);
4333 else if (rlen > 0) {
4335 pLF = IOBuf->buf + IOBuf->BufUsed;
4336 IOBuf->BufUsed += rlen;
4337 IOBuf->buf[IOBuf->BufUsed] = '\0';
4339 pche = IOBuf->buf + IOBuf->BufUsed;
4341 while ((pLF < pche) && (*pLF != '\n'))
4343 if ((pLF >= pche) || (*pLF == '\0'))
4346 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4350 if (pLF != NULL) apos = pLF - IOBuf->buf;
4351 IncreaseBuf(IOBuf, 1, -1);
4352 if (pLF != NULL) pLF = IOBuf->buf + apos;
4362 if (len > 0 && (*(pLF - 1) == '\r') )
4364 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4365 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4371 return retlen + len;
4373 *Error = ErrRBLF_NotEnoughSentFromServer;
4378 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4380 * @ingroup StrBuf_IO
4381 * @brief Input binary data from socket
4382 * flushes and closes the FD on error
4383 * @param Buf the buffer to get the input to
4384 * @param fd pointer to the filedescriptor to read
4385 * @param append Append to an existing string or replace?
4386 * @param nBytes the maximal number of bytes to read
4387 * @param Error strerror() on error
4388 * @returns numbers of chars read
4390 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4401 if ((Buf == NULL) || (*fd == -1))
4403 *Error = ErrRBLF_BLOBPreConditionFailed;
4408 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4409 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4411 ptr = Buf->buf + Buf->BufUsed;
4413 fdflags = fcntl(*fd, F_GETFL);
4414 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4416 while ((nRead < nBytes) &&
4426 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4427 *Error = strerror(errno);
4431 *Error = ErrRBLF_SelectFailed;
4434 if (! FD_ISSET(*fd, &rfds) != 0) {
4440 if ((rlen = read(*fd,
4442 nBytes - nRead)) == -1) {
4445 *Error = strerror(errno);
4450 Buf->BufUsed += rlen;
4452 Buf->buf[Buf->BufUsed] = '\0';
4456 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4457 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4459 * @ingroup StrBuf_BufferedIO
4460 * @brief Input binary data from socket
4461 * flushes and closes the FD on error
4462 * @param Blob put binary thing here
4463 * @param IOBuf the buffer to get the input to
4464 * @param Pos offset inside of IOBuf
4465 * @param fd pointer to the filedescriptor to read
4466 * @param append Append to an existing string or replace?
4467 * @param nBytes the maximal number of bytes to read
4468 * @param check whether we should search for '000\n' terminators in case of timeouts
4469 * @param Error strerror() on error
4470 * @returns numbers of chars read
4472 int StrBufReadBLOBBuffered(StrBuf *Blob,
4486 int nAlreadyRead = 0;
4491 int nSuccessLess = 0;
4494 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4498 *Error = ErrRBB_BLOBFPreConditionFailed;
4504 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4505 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4510 len = pos - IOBuf->buf;
4511 rlen = IOBuf->BufUsed - len;
4514 if ((IOBuf->BufUsed > 0) &&
4516 (pos < IOBuf->buf + IOBuf->BufUsed))
4518 if (rlen < nBytes) {
4519 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4520 Blob->BufUsed += rlen;
4521 Blob->buf[Blob->BufUsed] = '\0';
4522 nAlreadyRead = nRead = rlen;
4525 if (rlen >= nBytes) {
4526 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4527 Blob->BufUsed += nBytes;
4528 Blob->buf[Blob->BufUsed] = '\0';
4529 if (rlen == nBytes) {
4541 if (IOBuf->BufSize < nBytes - nRead)
4542 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4545 len = Blob->BufUsed;
4547 fdflags = fcntl(*fd, F_GETFL);
4548 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4556 while ((nSuccessLess < MaxTries) &&
4566 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4567 *Error = strerror(errno);
4571 *Error = ErrRBLF_SelectFailed;
4574 if (! FD_ISSET(*fd, &rfds) != 0) {
4581 IOBuf->BufSize - (ptr - IOBuf->buf));
4585 *Error = strerror(errno);
4588 else if (rlen == 0){
4589 if ((check == NNN_TERM) &&
4591 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4593 StrBufPlain(Blob, HKEY("\n000\n"));
4594 StrBufCutRight(Blob, 5);
4595 return Blob->BufUsed;
4597 else if (!IsNonBlock)
4599 else if (nSuccessLess > MaxTries) {
4601 *Error = ErrRBB_too_many_selects;
4605 else if (rlen > 0) {
4609 IOBuf->BufUsed += rlen;
4612 if (nSuccessLess >= MaxTries) {
4614 *Error = ErrRBB_too_many_selects;
4618 if (nRead > nBytes) {
4619 *Pos = IOBuf->buf + nBytes;
4621 Blob->buf[Blob->BufUsed] = '\0';
4622 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4626 return nRead + nAlreadyRead;
4630 * @ingroup StrBuf_IO
4631 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4632 * @param LineBuf your line will be copied here.
4633 * @param Buf BLOB with lines of text...
4634 * @param Ptr moved arround to keep the next-line across several iterations
4635 * has to be &NULL on start; will be &NotNULL on end of buffer
4636 * @returns size of remaining buffer
4638 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4640 const char *aptr, *ptr, *eptr;
4643 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4644 *Ptr = StrBufNOTNULL;
4648 FlushStrBuf(LineBuf);
4650 ptr = aptr = Buf->buf;
4654 optr = LineBuf->buf;
4655 eptr = Buf->buf + Buf->BufUsed;
4656 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4658 while ((ptr <= eptr) &&
4665 LineBuf->BufUsed = optr - LineBuf->buf;
4666 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4667 optr = LineBuf->buf + LineBuf->BufUsed;
4668 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4672 if ((ptr >= eptr) && (optr > LineBuf->buf))
4674 LineBuf->BufUsed = optr - LineBuf->buf;
4676 if ((ptr <= eptr) && (*ptr == '\r'))
4678 if ((ptr <= eptr) && (*ptr == '\n'))
4685 *Ptr = StrBufNOTNULL;
4688 return Buf->BufUsed - (ptr - Buf->buf);
4693 * @ingroup StrBuf_IO
4694 * @brief removes double slashes from pathnames
4695 * @param Dir directory string to filter
4696 * @param RemoveTrailingSlash allows / disallows trailing slashes
4698 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4704 while (!IsEmptyStr(a)) {
4716 if ((RemoveTrailingSlash) &&
4722 Dir->BufUsed = b - Dir->buf;