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"
49 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
50 const Bytef * source, uLong sourceLen, int level);
52 int BaseStrBufSize = 64;
55 const char *StrBufNOTNULL = ((char*) NULL) - 1;
57 const char HexList[256][3] = {
58 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
59 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
60 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
61 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
62 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
63 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
64 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
65 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
66 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
67 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
68 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
69 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
70 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
71 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
72 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
73 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
76 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
77 * StrBuf is a versatile class, aiding the handling of dynamic strings
78 * * reduce de/reallocations
79 * * reduce the need to remeasure it
80 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
81 * * allow asyncroneous IO for line and Blob based operations
82 * * reduce the use of memove in those
83 * * Quick filling in several operations with append functions
87 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
92 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
94 * use these operators to interfere with code demanding char*;
95 * if you need to own the content, smash me. Avoid, since we loose the length information.
99 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
101 * operations to get your Strings into a StrBuf, manipulating them, or appending
104 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
106 * Quick tokenizer; demands of the user to pull its tokens in sequence
110 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
112 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
116 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
118 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
119 * External Cursor to maintain the current read position inside of the buffer
120 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
124 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
130 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
132 * these functions translate the content of a buffer into another representation;
133 * some are combined Fillers and encoders
137 * Private Structure for the Stringbuffer
140 char *buf; /**< the pointer to the dynamic buffer */
141 long BufSize; /**< how many spcae do we optain */
142 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
143 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
145 long nIncreases; /**< for profiling; cound how many times we needed more */
146 char bt [SIZ]; /**< Stacktrace of last increase */
147 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
152 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
153 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
156 #ifdef HAVE_BACKTRACE
157 static void StrBufBacktrace(StrBuf *Buf, int which)
161 void *stack_frames[50];
166 pstart = pch = Buf->bt;
168 pstart = pch = Buf->bt_lastinc;
169 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
170 strings = backtrace_symbols(stack_frames, size);
171 for (i = 0; i < size; i++) {
173 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
175 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
184 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
186 if (hFreeDbglog == -1){
187 pid_t pid = getpid();
189 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
190 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
192 if ((*FreeMe)->nIncreases > 0)
196 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
198 (*FreeMe)->nIncreases,
202 (*FreeMe)->bt_lastinc);
203 n = write(hFreeDbglog, buf, n);
209 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
213 n = write(hFreeDbglog, buf, n);
217 void dbg_IncreaseBuf(StrBuf *IncMe)
220 #ifdef HAVE_BACKTRACE
221 StrBufBacktrace(Buf, 1);
225 void dbg_Init(StrBuf *Buf)
229 Buf->bt_lastinc[0] = '\0';
230 #ifdef HAVE_BACKTRACE
231 StrBufBacktrace(Buf, 0);
237 #define dbg_FreeStrBuf(a, b)
238 #define dbg_IncreaseBuf(a)
245 * @brief swaps the contents of two StrBufs
246 * this is to be used to have cheap switched between a work-buffer and a target buffer
248 * @param B second one
250 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
254 memcpy(&C, A, sizeof(*A));
255 memcpy(A, B, sizeof(*B));
256 memcpy(B, &C, sizeof(C));
261 * @ingroup StrBuf_Cast
262 * @brief Cast operator to Plain String
263 * @note if the buffer is altered by StrBuf operations, this pointer may become
264 * invalid. So don't lean on it after altering the buffer!
265 * Since this operation is considered cheap, rather call it often than risking
266 * your pointer to become invalid!
267 * @param Str the string we want to get the c-string representation for
268 * @returns the Pointer to the Content. Don't mess with it!
270 inline const char *ChrPtr(const StrBuf *Str)
278 * @ingroup StrBuf_Cast
279 * @brief since we know strlen()'s result, provide it here.
280 * @param Str the string to return the length to
281 * @returns contentlength of the buffer
283 inline int StrLength(const StrBuf *Str)
285 return (Str != NULL) ? Str->BufUsed : 0;
289 * @ingroup StrBuf_DeConstructors
290 * @brief local utility function to resize the buffer
291 * @param Buf the buffer whichs storage we should increase
292 * @param KeepOriginal should we copy the original buffer or just start over with a new one
293 * @param DestSize what should fit in after?
295 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
298 size_t NewSize = Buf->BufSize * 2;
304 while ((NewSize <= DestSize) && (NewSize != 0))
310 NewBuf= (char*) malloc(NewSize);
314 if (KeepOriginal && (Buf->BufUsed > 0))
316 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
325 Buf->BufSize = NewSize;
327 dbg_IncreaseBuf(Buf);
333 * @ingroup StrBuf_DeConstructors
334 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
335 * @param Buf Buffer to shrink (has to be empty)
336 * @param ThreshHold if the buffer is bigger then this, its readjusted
337 * @param NewSize if we Shrink it, how big are we going to be afterwards?
339 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
342 (Buf->BufUsed == 0) &&
343 (Buf->BufSize < ThreshHold)) {
345 Buf->buf = (char*) malloc(NewSize);
347 Buf->BufSize = NewSize;
352 * @ingroup StrBuf_DeConstructors
353 * @brief shrink long term buffers to their real size so they don't waste memory
354 * @param Buf buffer to shrink
355 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
356 * @returns physical size of the buffer
358 long StrBufShrinkToFit(StrBuf *Buf, int Force)
363 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
367 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
371 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
372 Buf->BufSize = Buf->BufUsed + 1;
380 * @ingroup StrBuf_DeConstructors
381 * @brief Allocate a new buffer with default buffer size
382 * @returns the new stringbuffer
384 StrBuf* NewStrBuf(void)
388 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
392 NewBuf->buf = (char*) malloc(BaseStrBufSize);
393 if (NewBuf->buf == NULL)
398 NewBuf->buf[0] = '\0';
399 NewBuf->BufSize = BaseStrBufSize;
401 NewBuf->ConstBuf = 0;
409 * @ingroup StrBuf_DeConstructors
410 * @brief Copy Constructor; returns a duplicate of CopyMe
411 * @param CopyMe Buffer to faxmilate
412 * @returns the new stringbuffer
414 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
421 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
425 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
426 if (NewBuf->buf == NULL)
432 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
433 NewBuf->BufUsed = CopyMe->BufUsed;
434 NewBuf->BufSize = CopyMe->BufSize;
435 NewBuf->ConstBuf = 0;
443 * @ingroup StrBuf_DeConstructors
444 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
445 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
446 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
447 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
448 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
449 * @returns the new stringbuffer
451 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
455 if (CreateRelpaceMe == NULL)
460 if (*CreateRelpaceMe != NULL)
461 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
463 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
467 if (CopyFlushMe == NULL)
469 if (*CreateRelpaceMe != NULL)
470 FlushStrBuf(*CreateRelpaceMe);
472 *CreateRelpaceMe = NewStrBuf();
477 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
478 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
479 * be a big IO-Buffer...
481 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
483 if (*CreateRelpaceMe == NULL)
485 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
490 NewBuf = *CreateRelpaceMe;
493 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
497 if (*CreateRelpaceMe == NULL)
499 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
503 NewBuf = *CreateRelpaceMe;
504 SwapBuffers (NewBuf, CopyFlushMe);
507 FlushStrBuf(CopyFlushMe);
512 * @ingroup StrBuf_DeConstructors
513 * @brief create a new Buffer using an existing c-string
514 * this function should also be used if you want to pre-suggest
515 * the buffer size to allocate in conjunction with ptr == NULL
516 * @param ptr the c-string to copy; may be NULL to create a blank instance
517 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
518 * @returns the new stringbuffer
520 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
523 size_t Siz = BaseStrBufSize;
526 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
531 CopySize = strlen((ptr != NULL)?ptr:"");
535 while ((Siz <= CopySize) && (Siz != 0))
544 NewBuf->buf = (char*) malloc(Siz);
545 if (NewBuf->buf == NULL)
550 NewBuf->BufSize = Siz;
552 memcpy(NewBuf->buf, ptr, CopySize);
553 NewBuf->buf[CopySize] = '\0';
554 NewBuf->BufUsed = CopySize;
557 NewBuf->buf[0] = '\0';
560 NewBuf->ConstBuf = 0;
568 * @ingroup StrBuf_DeConstructors
569 * @brief Set an existing buffer from a c-string
570 * @param Buf buffer to load
571 * @param ptr c-string to put into
572 * @param nChars set to -1 if we should work 0-terminated
573 * @returns the new length of the string
575 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
590 CopySize = strlen(ptr);
594 while ((Siz <= CopySize) && (Siz != 0))
602 if (Siz != Buf->BufSize)
603 IncreaseBuf(Buf, 0, Siz);
604 memcpy(Buf->buf, ptr, CopySize);
605 Buf->buf[CopySize] = '\0';
606 Buf->BufUsed = CopySize;
613 * @ingroup StrBuf_DeConstructors
614 * @brief use strbuf as wrapper for a string constant for easy handling
615 * @param StringConstant a string to wrap
616 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
618 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
622 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
625 NewBuf->buf = (char*) StringConstant;
626 NewBuf->BufSize = SizeOfStrConstant;
627 NewBuf->BufUsed = SizeOfStrConstant;
628 NewBuf->ConstBuf = 1;
637 * @ingroup StrBuf_DeConstructors
638 * @brief flush the content of a Buf; keep its struct
639 * @param buf Buffer to flush
641 int FlushStrBuf(StrBuf *buf)
643 if ((buf == NULL) || (buf->buf == NULL))
653 * @ingroup StrBuf_DeConstructors
654 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
655 * @param buf Buffer to wipe
657 int FLUSHStrBuf(StrBuf *buf)
663 if (buf->BufUsed > 0) {
664 memset(buf->buf, 0, buf->BufUsed);
671 int hFreeDbglog = -1;
674 * @ingroup StrBuf_DeConstructors
675 * @brief Release a Buffer
676 * Its a double pointer, so it can NULL your pointer
677 * so fancy SIG11 appear instead of random results
678 * @param FreeMe Pointer Pointer to the buffer to free
680 void FreeStrBuf (StrBuf **FreeMe)
685 dbg_FreeStrBuf(FreeMe, 'F');
687 if (!(*FreeMe)->ConstBuf)
688 free((*FreeMe)->buf);
694 * @ingroup StrBuf_DeConstructors
695 * @brief flatten a Buffer to the Char * we return
696 * Its a double pointer, so it can NULL your pointer
697 * so fancy SIG11 appear instead of random results
698 * The Callee then owns the buffer and is responsible for freeing it.
699 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
700 * @returns the pointer of the buffer; Callee owns the memory thereafter.
702 char *SmashStrBuf (StrBuf **SmashMe)
706 if ((SmashMe == NULL) || (*SmashMe == NULL))
709 dbg_FreeStrBuf(SmashMe, 'S');
711 Ret = (*SmashMe)->buf;
718 * @ingroup StrBuf_DeConstructors
719 * @brief Release the buffer
720 * If you want put your StrBuf into a Hash, use this as Destructor.
721 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
723 void HFreeStrBuf (void *VFreeMe)
725 StrBuf *FreeMe = (StrBuf*)VFreeMe;
729 dbg_FreeStrBuf(SmashMe, 'H');
731 if (!FreeMe->ConstBuf)
737 /*******************************************************************************
738 * Simple string transformations *
739 *******************************************************************************/
743 * @brief Wrapper around atol
745 long StrTol(const StrBuf *Buf)
750 return atol(Buf->buf);
757 * @brief Wrapper around atoi
759 int StrToi(const StrBuf *Buf)
763 if (Buf->BufUsed > 0)
764 return atoi(Buf->buf);
771 * @brief Checks to see if the string is a pure number
772 * @param Buf The buffer to inspect
773 * @returns 1 if its a pure number, 0, if not.
775 int StrBufIsNumber(const StrBuf *Buf) {
777 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
780 strtoll(Buf->buf, &pEnd, 10);
781 if (pEnd == Buf->buf)
783 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
785 if (Buf->buf == pEnd)
791 * @ingroup StrBuf_Filler
792 * @brief modifies a Single char of the Buf
793 * You can point to it via char* or a zero-based integer
794 * @param Buf The buffer to manipulate
795 * @param ptr char* to zero; use NULL if unused
796 * @param nThChar zero based pointer into the string; use -1 if unused
797 * @param PeekValue The Character to place into the position
799 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
804 nThChar = ptr - Buf->buf;
805 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
807 Buf->buf[nThChar] = PeekValue;
812 * @ingroup StrBuf_Filler
813 * @brief modifies a range of chars of the Buf
814 * You can point to it via char* or a zero-based integer
815 * @param Buf The buffer to manipulate
816 * @param ptr char* to zero; use NULL if unused
817 * @param nThChar zero based pointer into the string; use -1 if unused
818 * @param nChars how many chars are to be flushed?
819 * @param PookValue The Character to place into that area
821 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
826 nThChar = ptr - Buf->buf;
827 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
829 if (nThChar + nChars > Buf->BufUsed)
830 nChars = Buf->BufUsed - nThChar;
832 memset(Buf->buf + nThChar, PookValue, nChars);
833 /* just to be shure... */
834 Buf->buf[Buf->BufUsed] = 0;
839 * @ingroup StrBuf_Filler
840 * @brief Append a StringBuffer to the buffer
841 * @param Buf Buffer to modify
842 * @param AppendBuf Buffer to copy at the end of our buffer
843 * @param Offset Should we start copying from an offset?
845 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
847 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
848 (Buf == NULL) || (Buf->buf == NULL))
851 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
854 AppendBuf->BufUsed + Buf->BufUsed);
856 memcpy(Buf->buf + Buf->BufUsed,
857 AppendBuf->buf + Offset,
858 AppendBuf->BufUsed - Offset);
859 Buf->BufUsed += AppendBuf->BufUsed - Offset;
860 Buf->buf[Buf->BufUsed] = '\0';
865 * @ingroup StrBuf_Filler
866 * @brief Append a C-String to the buffer
867 * @param Buf Buffer to modify
868 * @param AppendBuf Buffer to copy at the end of our buffer
869 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
870 * @param Offset Should we start copying from an offset?
872 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
875 long BufSizeRequired;
877 if ((AppendBuf == NULL) || (Buf == NULL))
881 aps = strlen(AppendBuf + Offset);
883 aps = AppendSize - Offset;
885 BufSizeRequired = Buf->BufUsed + aps + 1;
886 if (Buf->BufSize <= BufSizeRequired)
887 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
889 memcpy(Buf->buf + Buf->BufUsed,
893 Buf->buf[Buf->BufUsed] = '\0';
897 * @ingroup StrBuf_Filler
898 * @brief sprintf like function appending the formated string to the buffer
899 * vsnprintf version to wrap into own calls
900 * @param Buf Buffer to extend by format and Params
901 * @param format printf alike format to add
902 * @param ap va_list containing the items for format
904 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
912 if ((Buf == NULL) || (format == NULL))
915 BufSize = Buf->BufSize;
916 nWritten = Buf->BufSize + 1;
917 Offset = Buf->BufUsed;
918 newused = Offset + nWritten;
920 while (newused >= BufSize) {
922 nWritten = vsnprintf(Buf->buf + Offset,
923 Buf->BufSize - Offset,
926 newused = Offset + nWritten;
927 if (newused >= Buf->BufSize) {
928 if (IncreaseBuf(Buf, 1, newused) == -1)
929 return; /* TODO: error handling? */
930 newused = Buf->BufSize + 1;
933 Buf->BufUsed = Offset + nWritten;
934 BufSize = Buf->BufSize;
941 * @ingroup StrBuf_Filler
942 * @brief sprintf like function appending the formated string to the buffer
943 * @param Buf Buffer to extend by format and Params
944 * @param format printf alike format to add
946 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
954 if ((Buf == NULL) || (format == NULL))
957 BufSize = Buf->BufSize;
958 nWritten = Buf->BufSize + 1;
959 Offset = Buf->BufUsed;
960 newused = Offset + nWritten;
962 while (newused >= BufSize) {
963 va_start(arg_ptr, format);
964 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
965 Buf->BufSize - Buf->BufUsed,
968 newused = Buf->BufUsed + nWritten;
969 if (newused >= Buf->BufSize) {
970 if (IncreaseBuf(Buf, 1, newused) == -1)
971 return; /* TODO: error handling? */
972 newused = Buf->BufSize + 1;
975 Buf->BufUsed += nWritten;
976 BufSize = Buf->BufSize;
983 * @ingroup StrBuf_Filler
984 * @brief sprintf like function putting the formated string into the buffer
985 * @param Buf Buffer to extend by format and Parameters
986 * @param format printf alike format to add
988 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
993 if ((Buf == NULL) || (format == NULL))
996 nWritten = Buf->BufSize + 1;
997 while (nWritten >= Buf->BufSize) {
998 va_start(arg_ptr, format);
999 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1001 if (nWritten >= Buf->BufSize) {
1002 if (IncreaseBuf(Buf, 0, 0) == -1)
1003 return; /* TODO: error handling? */
1004 nWritten = Buf->BufSize + 1;
1007 Buf->BufUsed = nWritten ;
1012 * @ingroup StrBuf_Filler
1013 * @brief Callback for cURL to append the webserver reply to a buffer
1014 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1015 * @param size pre-defined by the cURL API; see man 3 curl for mre info
1016 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1017 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1019 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1028 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1029 return size * nmemb;
1035 * @brief extracts a substring from Source into dest
1036 * @param dest buffer to place substring into
1037 * @param Source string to copy substring from
1038 * @param Offset chars to skip from start
1039 * @param nChars number of chars to copy
1040 * @returns the number of chars copied; may be different from nChars due to the size of Source
1042 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1044 size_t NCharsRemain;
1045 if (Offset > Source->BufUsed)
1051 if (Offset + nChars < Source->BufUsed)
1053 if ((nChars >= dest->BufSize) &&
1054 (IncreaseBuf(dest, 0, nChars + 1) == -1))
1056 memcpy(dest->buf, Source->buf + Offset, nChars);
1057 dest->BufUsed = nChars;
1058 dest->buf[dest->BufUsed] = '\0';
1061 NCharsRemain = Source->BufUsed - Offset;
1062 if ((NCharsRemain >= dest->BufSize) &&
1063 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1065 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1066 dest->BufUsed = NCharsRemain;
1067 dest->buf[dest->BufUsed] = '\0';
1068 return NCharsRemain;
1073 * @brief Cut nChars from the start of the string
1074 * @param Buf Buffer to modify
1075 * @param nChars how many chars should be skipped?
1077 void StrBufCutLeft(StrBuf *Buf, int nChars)
1079 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1080 if (nChars >= Buf->BufUsed) {
1084 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1085 Buf->BufUsed -= nChars;
1086 Buf->buf[Buf->BufUsed] = '\0';
1091 * @brief Cut the trailing n Chars from the string
1092 * @param Buf Buffer to modify
1093 * @param nChars how many chars should be trunkated?
1095 void StrBufCutRight(StrBuf *Buf, int nChars)
1097 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1100 if (nChars >= Buf->BufUsed) {
1104 Buf->BufUsed -= nChars;
1105 Buf->buf[Buf->BufUsed] = '\0';
1110 * @brief Cut the string after n Chars
1111 * @param Buf Buffer to modify
1112 * @param AfternChars after how many chars should we trunkate the string?
1113 * @param At if non-null and points inside of our string, cut it there.
1115 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1117 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1119 AfternChars = At - Buf->buf;
1122 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1124 Buf->BufUsed = AfternChars;
1125 Buf->buf[Buf->BufUsed] = '\0';
1131 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1132 * @param Buf the string to modify
1134 void StrBufTrim(StrBuf *Buf)
1137 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1139 while ((Buf->BufUsed > 0) &&
1140 isspace(Buf->buf[Buf->BufUsed - 1]))
1144 Buf->buf[Buf->BufUsed] = '\0';
1146 if (Buf->BufUsed == 0) return;
1148 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1151 if (delta > 0) StrBufCutLeft(Buf, delta);
1155 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1156 * @param Buf the string to modify
1158 void StrBufSpaceToBlank(StrBuf *Buf)
1162 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1165 pche = pch + Buf->BufUsed;
1174 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1180 if ((Buf == NULL) || (Buf->buf == NULL))
1182 pLeft = pBuff = Buf->buf;
1183 while (pBuff != NULL) {
1185 pBuff = strchr(pBuff, leftboundary);
1194 pRight = strchr(pBuff, rightboundary);
1196 StrBufCutAt(Buf, 0, pRight);
1198 StrBufCutLeft(Buf, pLeft - Buf->buf);
1203 * @ingroup StrBuf_Filler
1204 * @brief uppercase the contents of a buffer
1205 * @param Buf the buffer to translate
1207 void StrBufUpCase(StrBuf *Buf)
1211 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1214 pche = pch + Buf->BufUsed;
1215 while (pch < pche) {
1216 *pch = toupper(*pch);
1223 * @ingroup StrBuf_Filler
1224 * @brief lowercase the contents of a buffer
1225 * @param Buf the buffer to translate
1227 void StrBufLowerCase(StrBuf *Buf)
1231 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1234 pche = pch + Buf->BufUsed;
1235 while (pch < pche) {
1236 *pch = tolower(*pch);
1242 /*******************************************************************************
1243 * a tokenizer that kills, maims, and destroys *
1244 *******************************************************************************/
1247 * @ingroup StrBuf_Tokenizer
1248 * @brief Replace a token at a given place with a given length by another token with given length
1249 * @param Buf String where to work on
1250 * @param where where inside of the Buf is the search-token
1251 * @param HowLong How long is the token to be replaced
1252 * @param Repl Token to insert at 'where'
1253 * @param ReplLen Length of repl
1254 * @returns -1 if fail else length of resulting Buf
1256 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1257 const char *Repl, long ReplLen)
1260 if ((Buf == NULL) ||
1261 (where > Buf->BufUsed) ||
1262 (where + HowLong > Buf->BufUsed))
1265 if (where + ReplLen - HowLong > Buf->BufSize)
1266 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1269 memmove(Buf->buf + where + ReplLen,
1270 Buf->buf + where + HowLong,
1271 Buf->BufUsed - where - HowLong);
1273 memcpy(Buf->buf + where,
1276 Buf->BufUsed += ReplLen - HowLong;
1278 return Buf->BufUsed;
1282 * @ingroup StrBuf_Tokenizer
1283 * @brief Counts the numbmer of tokens in a buffer
1284 * @param source String to count tokens in
1285 * @param tok Tokenizer char to count
1286 * @returns numbers of tokenizer chars found
1288 int StrBufNum_tokens(const StrBuf *source, char tok)
1292 if ((source == NULL) || (source->BufUsed == 0))
1294 if ((source->BufUsed == 1) && (*source->buf == tok))
1298 pche = pch + source->BufUsed;
1309 * @ingroup StrBuf_Tokenizer
1310 * @brief a string tokenizer
1311 * @param Source StringBuffer to read into
1312 * @param parmnum n'th Parameter to remove
1313 * @param separator tokenizer character
1314 * @returns -1 if not found, else length of token.
1316 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1319 char *d, *s, *end; /* dest, source */
1322 /* Find desired @parameter */
1323 end = Source->buf + Source->BufUsed;
1325 while ((d <= end) &&
1328 /* End of string, bail! */
1333 if (*d == separator) {
1338 if ((d == NULL) || (d >= end))
1339 return 0; /* @Parameter not found */
1341 /* Find next @parameter */
1343 while ((s <= end) &&
1344 (*s && *s != separator))
1348 if (*s == separator)
1352 /* Hack and slash */
1357 memmove(d, s, Source->BufUsed - (s - Source->buf));
1358 Source->BufUsed += ReducedBy;
1359 Source->buf[Source->BufUsed] = '\0';
1361 else if (d == Source->buf) {
1363 Source->BufUsed = 0;
1367 Source->BufUsed += ReducedBy;
1378 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1380 const StrBuf Temp = {
1393 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1397 * @ingroup StrBuf_Tokenizer
1398 * @brief a string tokenizer
1399 * @param dest Destination StringBuffer
1400 * @param Source StringBuffer to read into
1401 * @param parmnum n'th Parameter to extract
1402 * @param separator tokenizer character
1403 * @returns -1 if not found, else length of token.
1405 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1407 const char *s, *e; //* source * /
1408 int len = 0; //* running total length of extracted string * /
1409 int current_token = 0; //* token currently being processed * /
1412 dest->buf[0] = '\0';
1418 if ((Source == NULL) || (Source->BufUsed ==0)) {
1422 e = s + Source->BufUsed;
1425 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1427 while ((s < e) && !IsEmptyStr(s)) {
1428 if (*s == separator) {
1431 if (len >= dest->BufSize) {
1432 dest->BufUsed = len;
1433 if (IncreaseBuf(dest, 1, -1) < 0) {
1438 if ( (current_token == parmnum) &&
1439 (*s != separator)) {
1440 dest->buf[len] = *s;
1443 else if (current_token > parmnum) {
1449 dest->buf[len] = '\0';
1450 dest->BufUsed = len;
1452 if (current_token < parmnum) {
1453 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1456 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1465 * @ingroup StrBuf_Tokenizer
1466 * @brief a string tokenizer to fetch an integer
1467 * @param Source String containing tokens
1468 * @param parmnum n'th Parameter to extract
1469 * @param separator tokenizer character
1470 * @returns 0 if not found, else integer representation of the token
1472 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1482 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1489 * @ingroup StrBuf_Tokenizer
1490 * @brief a string tokenizer to fetch a long integer
1491 * @param Source String containing tokens
1492 * @param parmnum n'th Parameter to extract
1493 * @param separator tokenizer character
1494 * @returns 0 if not found, else long integer representation of the token
1496 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1506 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1514 * @ingroup StrBuf_Tokenizer
1515 * @brief a string tokenizer to fetch an unsigned long
1516 * @param Source String containing tokens
1517 * @param parmnum n'th Parameter to extract
1518 * @param separator tokenizer character
1519 * @returns 0 if not found, else unsigned long representation of the token
1521 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1532 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1536 return (unsigned long) atol(pnum);
1545 * @ingroup StrBuf_NextTokenizer
1546 * @brief a string tokenizer; Bounds checker
1547 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1548 * @param Source our tokenbuffer
1549 * @param pStart the token iterator pointer to inspect
1550 * @returns whether the revolving pointer is inside of the search range
1552 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1554 if ((Source == NULL) ||
1555 (*pStart == StrBufNOTNULL) ||
1556 (Source->BufUsed == 0))
1560 if (*pStart == NULL)
1564 else if (*pStart > Source->buf + Source->BufUsed)
1568 else if (*pStart <= Source->buf)
1577 * @ingroup StrBuf_NextTokenizer
1578 * @brief a string tokenizer
1579 * @param dest Destination StringBuffer
1580 * @param Source StringBuffer to read into
1581 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1582 * @param separator tokenizer
1583 * @returns -1 if not found, else length of token.
1585 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1587 const char *s; /* source */
1588 const char *EndBuffer; /* end stop of source buffer */
1589 int current_token = 0; /* token currently being processed */
1590 int len = 0; /* running total length of extracted string */
1592 if ((Source == NULL) ||
1593 (Source->BufUsed == 0) )
1595 *pStart = StrBufNOTNULL;
1601 EndBuffer = Source->buf + Source->BufUsed;
1605 dest->buf[0] = '\0';
1610 *pStart = EndBuffer + 1;
1614 if (*pStart == NULL)
1616 *pStart = Source->buf; /* we're starting to examine this buffer. */
1618 else if ((*pStart < Source->buf) ||
1619 (*pStart > EndBuffer ) )
1621 return -1; /* no more tokens to find. */
1625 /* start to find the next token */
1626 while ((s <= EndBuffer) &&
1627 (current_token == 0) )
1629 if (*s == separator)
1631 /* we found the next token */
1635 if (len >= dest->BufSize)
1637 /* our Dest-buffer isn't big enough, increase it. */
1638 dest->BufUsed = len;
1640 if (IncreaseBuf(dest, 1, -1) < 0) {
1641 /* WHUT? no more mem? bail out. */
1648 if ( (current_token == 0 ) && /* are we in our target token? */
1649 (!IsEmptyStr(s) ) &&
1650 (separator != *s) ) /* don't copy the token itself */
1652 dest->buf[len] = *s; /* Copy the payload */
1653 ++len; /* remember the bigger size. */
1659 /* did we reach the end? */
1660 if ((s > EndBuffer)) {
1661 EndBuffer = StrBufNOTNULL;
1662 *pStart = EndBuffer;
1665 *pStart = s; /* remember the position for the next run */
1668 /* sanitize our extracted token */
1669 dest->buf[len] = '\0';
1670 dest->BufUsed = len;
1677 * @ingroup StrBuf_NextTokenizer
1678 * @brief a string tokenizer
1679 * @param Source StringBuffer to read from
1680 * @param pStart pointer to the end of the last token. Feed with NULL.
1681 * @param separator tokenizer character
1682 * @param nTokens number of tokens to fastforward over
1683 * @returns -1 if not found, else length of token.
1685 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1687 const char *s, *EndBuffer; //* source * /
1688 int len = 0; //* running total length of extracted string * /
1689 int current_token = 0; //* token currently being processed * /
1691 if ((Source == NULL) ||
1692 (Source->BufUsed ==0)) {
1696 return Source->BufUsed;
1698 if (*pStart == NULL)
1699 *pStart = Source->buf;
1701 EndBuffer = Source->buf + Source->BufUsed;
1703 if ((*pStart < Source->buf) ||
1704 (*pStart > EndBuffer)) {
1712 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1714 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1715 if (*s == separator) {
1718 if (current_token >= nTokens) {
1730 * @ingroup StrBuf_NextTokenizer
1731 * @brief a string tokenizer to fetch an integer
1732 * @param Source StringBuffer to read from
1733 * @param pStart Cursor on the tokenstring
1734 * @param separator tokenizer character
1735 * @returns 0 if not found, else integer representation of the token
1737 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1747 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1754 * @ingroup StrBuf_NextTokenizer
1755 * @brief a string tokenizer to fetch a long integer
1756 * @param Source StringBuffer to read from
1757 * @param pStart Cursor on the tokenstring
1758 * @param separator tokenizer character
1759 * @returns 0 if not found, else long integer representation of the token
1761 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1771 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1779 * @ingroup StrBuf_NextTokenizer
1780 * @brief a string tokenizer to fetch an unsigned long
1781 * @param Source StringBuffer to read from
1782 * @param pStart Cursor on the tokenstring
1783 * @param separator tokenizer character
1784 * @returns 0 if not found, else unsigned long representation of the token
1786 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1797 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1801 return (unsigned long) atol(pnum);
1811 /*******************************************************************************
1812 * Escape Appending *
1813 *******************************************************************************/
1816 * @ingroup StrBuf_DeEnCoder
1817 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1818 * @param OutBuf the output buffer
1819 * @param In Buffer to encode
1820 * @param PlainIn way in from plain old c strings
1822 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1824 const char *pch, *pche;
1828 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1830 if (PlainIn != NULL) {
1831 len = strlen(PlainIn);
1837 pche = pch + In->BufUsed;
1844 pt = OutBuf->buf + OutBuf->BufUsed;
1845 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1847 while (pch < pche) {
1849 IncreaseBuf(OutBuf, 1, -1);
1850 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1851 pt = OutBuf->buf + OutBuf->BufUsed;
1854 if((*pch >= 'a' && *pch <= 'z') ||
1855 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1856 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1857 (*pch == '!') || (*pch == '_') ||
1858 (*pch == ',') || (*pch == '.'))
1865 *(pt + 1) = HexList[(unsigned char)*pch][0];
1866 *(pt + 2) = HexList[(unsigned char)*pch][1];
1868 OutBuf->BufUsed += 3;
1876 * @ingroup StrBuf_DeEnCoder
1877 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1878 * @param OutBuf the output buffer
1879 * @param In Buffer to encode
1880 * @param PlainIn way in from plain old c strings
1882 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1884 const char *pch, *pche;
1888 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1890 if (PlainIn != NULL) {
1891 len = strlen(PlainIn);
1897 pche = pch + In->BufUsed;
1904 pt = OutBuf->buf + OutBuf->BufUsed;
1905 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1907 while (pch < pche) {
1909 IncreaseBuf(OutBuf, 1, -1);
1910 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1911 pt = OutBuf->buf + OutBuf->BufUsed;
1914 if((*pch >= 'a' && *pch <= 'z') ||
1915 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1916 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1917 (*pch == '!') || (*pch == '_') ||
1918 (*pch == ',') || (*pch == '.'))
1925 *(pt + 1) = HexList[(unsigned char)*pch][0];
1926 *(pt + 2) = HexList[(unsigned char)*pch][1];
1928 OutBuf->BufUsed += 3;
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
1941 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1943 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1945 const unsigned char *pch, *pche;
1949 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1951 if (PlainIn != NULL) {
1953 len = strlen((const char*)PlainIn);
1960 pch = (const unsigned char*)In->buf;
1961 pche = pch + In->BufUsed;
1968 pt = OutBuf->buf + OutBuf->BufUsed;
1969 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1971 while (pch < pche) {
1973 IncreaseBuf(OutBuf, 1, -1);
1974 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1975 pt = OutBuf->buf + OutBuf->BufUsed;
1978 *pt = HexList[*pch][0];
1980 *pt = HexList[*pch][1];
1981 pt ++; pch ++; OutBuf->BufUsed += 2;
1987 * @ingroup StrBuf_DeEnCoder
1988 * @brief append a string in hex encoding to the buffer
1989 * @param OutBuf the output buffer
1990 * @param In Buffer to encode
1991 * @param PlainIn way in from plain old c strings
1993 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1995 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1999 * @ingroup StrBuf_DeEnCoder
2000 * @brief Append a string, escaping characters which have meaning in HTML.
2002 * @param Target target buffer
2003 * @param Source source buffer; set to NULL if you just have a C-String
2004 * @param PlainIn Plain-C string to append; set to NULL if unused
2005 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2006 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2007 * if set to 2, linebreaks are replaced by <br/>
2009 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2011 const char *aptr, *eiptr;
2015 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2018 if (PlainIn != NULL) {
2020 len = strlen(PlainIn);
2025 eiptr = aptr + Source->BufUsed;
2026 len = Source->BufUsed;
2032 bptr = Target->buf + Target->BufUsed;
2033 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2035 while (aptr < eiptr){
2037 IncreaseBuf(Target, 1, -1);
2038 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2039 bptr = Target->buf + Target->BufUsed;
2042 memcpy(bptr, "<", 4);
2044 Target->BufUsed += 4;
2046 else if (*aptr == '>') {
2047 memcpy(bptr, ">", 4);
2049 Target->BufUsed += 4;
2051 else if (*aptr == '&') {
2052 memcpy(bptr, "&", 5);
2054 Target->BufUsed += 5;
2056 else if (*aptr == '"') {
2057 memcpy(bptr, """, 6);
2059 Target->BufUsed += 6;
2061 else if (*aptr == '\'') {
2062 memcpy(bptr, "'", 5);
2064 Target->BufUsed += 5;
2066 else if (*aptr == LB) {
2071 else if (*aptr == RB) {
2076 else if (*aptr == QU) {
2081 else if ((*aptr == 32) && (nbsp == 1)) {
2082 memcpy(bptr, " ", 6);
2084 Target->BufUsed += 6;
2086 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2087 *bptr='\0'; /* nothing */
2089 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2090 memcpy(bptr, "<br/>", 11);
2092 Target->BufUsed += 11;
2096 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2097 *bptr='\0'; /* nothing */
2107 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2109 return Target->BufUsed;
2113 * @ingroup StrBuf_DeEnCoder
2114 * @brief Append a string, escaping characters which have meaning in HTML.
2115 * Converts linebreaks into blanks; escapes single quotes
2116 * @param Target target buffer
2117 * @param Source source buffer; set to NULL if you just have a C-String
2118 * @param PlainIn Plain-C string to append; set to NULL if unused
2120 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2122 const char *aptr, *eiptr;
2126 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2129 if (PlainIn != NULL) {
2131 len = strlen(PlainIn);
2136 eiptr = aptr + Source->BufUsed;
2137 len = Source->BufUsed;
2143 eptr = Target->buf + Target->BufSize - 8;
2144 tptr = Target->buf + Target->BufUsed;
2146 while (aptr < eiptr){
2148 IncreaseBuf(Target, 1, -1);
2149 eptr = Target->buf + Target->BufSize - 8;
2150 tptr = Target->buf + Target->BufUsed;
2153 if (*aptr == '\n') {
2157 else if (*aptr == '\r') {
2161 else if (*aptr == '\'') {
2167 Target->BufUsed += 5;
2180 * @ingroup StrBuf_DeEnCoder
2181 * @brief Append a string, escaping characters which have meaning in ICAL.
2183 * @param Target target buffer
2184 * @param Source source buffer; set to NULL if you just have a C-String
2185 * @param PlainIn Plain-C string to append; set to NULL if unused
2187 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2189 const char *aptr, *eiptr;
2193 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2196 if (PlainIn != NULL) {
2198 len = strlen(PlainIn);
2203 eiptr = aptr + Source->BufUsed;
2204 len = Source->BufUsed;
2210 eptr = Target->buf + Target->BufSize - 8;
2211 tptr = Target->buf + Target->BufUsed;
2213 while (aptr < eiptr){
2214 if(tptr + 3 >= eptr) {
2215 IncreaseBuf(Target, 1, -1);
2216 eptr = Target->buf + Target->BufSize - 8;
2217 tptr = Target->buf + Target->BufUsed;
2220 if (*aptr == '\n') {
2227 else if (*aptr == '\r') {
2234 else if (*aptr == ',') {
2250 * @ingroup StrBuf_DeEnCoder
2251 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2253 * @param Target target buffer
2254 * @param Source source buffer; set to NULL if you just have a C-String
2255 * @param PlainIn Plain-C string to append; set to NULL if unused
2256 * @returns size of result or -1
2258 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2260 const char *aptr, *eiptr;
2265 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2268 if (PlainIn != NULL) {
2270 len = strlen(PlainIn);
2275 eiptr = aptr + Source->BufUsed;
2276 len = Source->BufUsed;
2282 bptr = Target->buf + Target->BufUsed;
2283 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2285 while (aptr < eiptr){
2287 IncreaseBuf(Target, 1, -1);
2288 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2289 bptr = Target->buf + Target->BufUsed;
2293 memcpy(bptr, HKEY("\\n"));
2295 Target->BufUsed += 2;
2298 memcpy(bptr, HKEY("\\r"));
2300 Target->BufUsed += 2;
2307 Target->BufUsed += 2;
2310 if ((*(aptr + 1) == 'u') &&
2311 isxdigit(*(aptr + 2)) &&
2312 isxdigit(*(aptr + 3)) &&
2313 isxdigit(*(aptr + 4)) &&
2314 isxdigit(*(aptr + 5)))
2315 { /* oh, a unicode escaper. let it pass through. */
2316 memcpy(bptr, aptr, 6);
2319 Target->BufUsed += 6;
2327 Target->BufUsed += 2;
2335 Target->BufUsed += 2;
2342 Target->BufUsed += 2;
2349 Target->BufUsed += 2;
2352 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2353 while (IsUtf8Sequence > 0){
2356 if (--IsUtf8Sequence)
2364 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2366 return Target->BufUsed;
2370 * @ingroup StrBuf_DeEnCoder
2371 * @brief Append a string, escaping characters which have meaning in HTML + json.
2373 * @param Target target buffer
2374 * @param Source source buffer; set to NULL if you just have a C-String
2375 * @param PlainIn Plain-C string to append; set to NULL if unused
2376 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2377 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2378 * if set to 2, linebreaks are replaced by <br/>
2380 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2382 const char *aptr, *eiptr;
2385 int IsUtf8Sequence = 0;
2387 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2390 if (PlainIn != NULL) {
2392 len = strlen(PlainIn);
2397 eiptr = aptr + Source->BufUsed;
2398 len = Source->BufUsed;
2404 bptr = Target->buf + Target->BufUsed;
2405 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2407 while (aptr < eiptr){
2409 IncreaseBuf(Target, 1, -1);
2410 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2411 bptr = Target->buf + Target->BufUsed;
2415 memcpy(bptr, HKEY("<"));
2417 Target->BufUsed += 4;
2420 memcpy(bptr, HKEY(">"));
2422 Target->BufUsed += 4;
2425 memcpy(bptr, HKEY("&"));
2427 Target->BufUsed += 5;
2440 switch (nolinebreaks) {
2442 *bptr='\0'; /* nothing */
2445 memcpy(bptr, HKEY("<br/>"));
2447 Target->BufUsed += 11;
2450 memcpy(bptr, HKEY("\\n"));
2452 Target->BufUsed += 2;
2456 switch (nolinebreaks) {
2459 *bptr='\0'; /* nothing */
2462 memcpy(bptr, HKEY("\\r"));
2464 Target->BufUsed += 2;
2474 Target->BufUsed += 2;
2477 if ((*(aptr + 1) == 'u') &&
2478 isxdigit(*(aptr + 2)) &&
2479 isxdigit(*(aptr + 3)) &&
2480 isxdigit(*(aptr + 4)) &&
2481 isxdigit(*(aptr + 5)))
2482 { /* oh, a unicode escaper. let it pass through. */
2483 memcpy(bptr, aptr, 6);
2486 Target->BufUsed += 6;
2494 Target->BufUsed += 2;
2502 Target->BufUsed += 2;
2509 Target->BufUsed += 2;
2516 Target->BufUsed += 2;
2520 memcpy(bptr, HKEY(" "));
2522 Target->BufUsed += 6;
2526 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2527 while (IsUtf8Sequence > 0){
2530 if (--IsUtf8Sequence)
2538 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2540 return Target->BufUsed;
2545 * @ingroup StrBuf_DeEnCoder
2546 * @brief replace all non-Ascii characters by another
2547 * @param Buf buffer to inspect
2548 * @param repl charater to stamp over non ascii chars
2550 void StrBufAsciify(StrBuf *Buf, const char repl)
2554 for (offset = 0; offset < Buf->BufUsed; offset ++)
2555 if (!isascii(Buf->buf[offset]))
2556 Buf->buf[offset] = repl;
2561 * @ingroup StrBuf_DeEnCoder
2562 * @brief unhide special chars hidden to the HTML escaper
2563 * @param target buffer to put the unescaped string in
2564 * @param source buffer to unescape
2566 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2571 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2577 FlushStrBuf(target);
2579 len = source->BufUsed;
2580 for (a = 0; a < len; ++a) {
2581 if (target->BufUsed >= target->BufSize)
2582 IncreaseBuf(target, 1, -1);
2584 if (source->buf[a] == '=') {
2585 hex[0] = source->buf[a + 1];
2586 hex[1] = source->buf[a + 2];
2589 sscanf(hex, "%02x", &b);
2590 target->buf[target->BufUsed] = b;
2591 target->buf[++target->BufUsed] = 0;
2595 target->buf[target->BufUsed] = source->buf[a];
2596 target->buf[++target->BufUsed] = 0;
2603 * @ingroup StrBuf_DeEnCoder
2604 * @brief hide special chars from the HTML escapers and friends
2605 * @param target buffer to put the escaped string in
2606 * @param source buffer to escape
2608 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2613 FlushStrBuf(target);
2615 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2620 len = source->BufUsed;
2621 for (i=0; i<len; ++i) {
2622 if (target->BufUsed + 4 >= target->BufSize)
2623 IncreaseBuf(target, 1, -1);
2624 if ( (isalnum(source->buf[i])) ||
2625 (source->buf[i]=='-') ||
2626 (source->buf[i]=='_') ) {
2627 target->buf[target->BufUsed++] = source->buf[i];
2630 sprintf(&target->buf[target->BufUsed],
2632 (0xFF &source->buf[i]));
2633 target->BufUsed += 3;
2636 target->buf[target->BufUsed + 1] = '\0';
2640 /*******************************************************************************
2641 * Quoted Printable de/encoding *
2642 *******************************************************************************/
2645 * @ingroup StrBuf_DeEnCoder
2646 * @brief decode a buffer from base 64 encoding; destroys original
2647 * @param Buf Buffor to transform
2649 int StrBufDecodeBase64(StrBuf *Buf)
2657 xferbuf = (char*) malloc(Buf->BufSize);
2658 if (xferbuf == NULL)
2662 siz = CtdlDecodeBase64(xferbuf,
2672 * @ingroup StrBuf_DeEnCoder
2673 * @brief decode a buffer from base 64 encoding; destroys original
2674 * @param Buf Buffor to transform
2676 int StrBufDecodeHex(StrBuf *Buf)
2679 char *pch, *pche, *pchi;
2681 if (Buf == NULL) return -1;
2683 pch = pchi = Buf->buf;
2684 pche = pch + Buf->BufUsed;
2686 while (pchi < pche){
2687 ch = decode_hex(pchi);
2694 Buf->BufUsed = pch - Buf->buf;
2695 return Buf->BufUsed;
2699 * @ingroup StrBuf_DeEnCoder
2700 * @brief replace all chars >0x20 && < 0x7F with Mute
2701 * @param Mute char to put over invalid chars
2702 * @param Buf Buffor to transform
2704 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2708 if (Buf == NULL) return -1;
2709 pch = (unsigned char *)Buf->buf;
2710 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2711 if ((*pch < 0x20) || (*pch > 0x7F))
2715 return Buf->BufUsed;
2720 * @ingroup StrBuf_DeEnCoder
2721 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2722 * @param Buf Buffer to translate
2723 * @param StripBlanks Reduce several blanks to one?
2725 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2734 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2735 Buf->buf[Buf->BufUsed - 1] = '\0';
2740 while (a < Buf->BufUsed) {
2741 if (Buf->buf[a] == '+')
2743 else if (Buf->buf[a] == '%') {
2744 /* don't let % chars through, rather truncate the input. */
2745 if (a + 2 > Buf->BufUsed) {
2750 hex[0] = Buf->buf[a + 1];
2751 hex[1] = Buf->buf[a + 2];
2754 sscanf(hex, "%02x", &b);
2755 Buf->buf[a] = (char) b;
2756 len = Buf->BufUsed - a - 2;
2758 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2770 * @ingroup StrBuf_DeEnCoder
2771 * @brief RFC2047-encode a header field if necessary.
2772 * If no non-ASCII characters are found, the string
2773 * will be copied verbatim without encoding.
2775 * @param target Target buffer.
2776 * @param source Source string to be encoded.
2777 * @returns encoded length; -1 if non success.
2779 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2781 const char headerStr[] = "=?UTF-8?Q?";
2782 int need_to_encode = 0;
2786 if ((source == NULL) ||
2790 while ((i < source->BufUsed) &&
2791 (!IsEmptyStr (&source->buf[i])) &&
2792 (need_to_encode == 0)) {
2793 if (((unsigned char) source->buf[i] < 32) ||
2794 ((unsigned char) source->buf[i] > 126)) {
2800 if (!need_to_encode) {
2801 if (*target == NULL) {
2802 *target = NewStrBufPlain(source->buf, source->BufUsed);
2805 FlushStrBuf(*target);
2806 StrBufAppendBuf(*target, source, 0);
2809 return (*target)->BufUsed;
2813 if (*target == NULL)
2814 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2815 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2816 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2817 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2818 (*target)->BufUsed = sizeof(headerStr) - 1;
2819 for (i=0; (i < source->BufUsed); ++i) {
2820 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2821 IncreaseBuf(*target, 1, 0);
2822 ch = (unsigned char) source->buf[i];
2832 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2833 (*target)->BufUsed += 3;
2837 (*target)->buf[(*target)->BufUsed] = '_';
2839 (*target)->buf[(*target)->BufUsed] = ch;
2840 (*target)->BufUsed++;
2844 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2845 IncreaseBuf(*target, 1, 0);
2847 (*target)->buf[(*target)->BufUsed++] = '?';
2848 (*target)->buf[(*target)->BufUsed++] = '=';
2849 (*target)->buf[(*target)->BufUsed] = '\0';
2850 return (*target)->BufUsed;;
2855 static void AddRecipient(StrBuf *Target,
2857 StrBuf *EmailAddress,
2862 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2863 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2865 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2866 StrBufRFC2047encode(&EncBuf, UserName);
2867 StrBufAppendBuf(Target, EncBuf, 0);
2868 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2869 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2871 if (StrLength(EmailAddress) > 0){
2872 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2873 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2874 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2880 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2881 * \param Recp Source list of email recipients
2882 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2883 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2884 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2885 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2887 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2889 StrBuf *EmailAddress,
2893 const char *pch, *pche;
2894 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2896 if ((Recp == NULL) || (StrLength(Recp) == 0))
2900 pche = pch + StrLength(Recp);
2902 if (!CheckEncode(pch, -1, pche))
2903 return NewStrBufDup(Recp);
2905 Target = NewStrBufPlain(NULL, StrLength(Recp));
2907 while ((pch != NULL) && (pch < pche))
2909 while (isspace(*pch)) pch++;
2910 UserEnd = EmailStart = EmailEnd = NULL;
2912 if ((*pch == '"') || (*pch == '\'')) {
2913 UserStart = pch + 1;
2915 UserEnd = strchr(UserStart, *pch);
2916 if (UserEnd == NULL)
2917 break; ///TODO: Userfeedback??
2918 EmailStart = UserEnd + 1;
2919 while (isspace(*EmailStart))
2921 if (UserEnd == UserStart) {
2922 UserStart = UserEnd = NULL;
2925 if (*EmailStart == '<') {
2927 EmailEnd = strchr(EmailStart, '>');
2928 if (EmailEnd == NULL)
2929 EmailEnd = strchr(EmailStart, ',');
2933 EmailEnd = strchr(EmailStart, ',');
2935 if (EmailEnd == NULL)
2942 EmailEnd = strchr(UserStart, ',');
2943 if (EmailEnd == NULL) {
2944 EmailEnd = strchr(pch, '>');
2946 if (EmailEnd != NULL) {
2956 while ((EmailEnd > UserStart) && !gt &&
2957 ((*EmailEnd == ',') ||
2958 (*EmailEnd == '>') ||
2959 (isspace(*EmailEnd))))
2961 if (*EmailEnd == '>')
2966 if (EmailEnd == UserStart)
2970 EmailStart = strchr(UserStart, '<');
2971 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2973 UserEnd = EmailStart;
2975 while ((UserEnd > UserStart) &&
2976 isspace (*(UserEnd - 1)))
2979 if (UserStart >= UserEnd)
2980 UserStart = UserEnd = NULL;
2982 else { /* this is a local recipient... no domain, just a realname */
2983 EmailStart = UserStart;
2984 At = strchr(EmailStart, '@');
2990 EmailStart = UserStart;
2996 if ((UserStart != NULL) && (UserEnd != NULL))
2997 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2998 else if ((UserStart != NULL) && (UserEnd == NULL))
2999 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3001 FlushStrBuf(UserName);
3003 if ((EmailStart != NULL) && (EmailEnd != NULL))
3004 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3005 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3006 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3008 FlushStrBuf(EmailAddress);
3010 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3015 if ((pch != NULL) && (*pch == ','))
3017 if (pch != NULL) while (isspace(*pch))
3026 * @brief replaces all occurances of 'search' by 'replace'
3027 * @param buf Buffer to modify
3028 * @param search character to search
3029 * @param replace character to replace search by
3031 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3036 for (i=0; i<buf->BufUsed; i++)
3037 if (buf->buf[i] == search)
3038 buf->buf[i] = replace;
3044 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3045 * @param buf Buffer to modify
3047 void StrBufToUnixLF(StrBuf *buf)
3049 char *pche, *pchS, *pchT;
3053 pche = buf->buf + buf->BufUsed;
3054 pchS = pchT = buf->buf;
3060 if (*pchS != '\n') {
3069 buf->BufUsed = pchT - buf->buf;
3073 /*******************************************************************************
3074 * Iconv Wrapper; RFC822 de/encoding *
3075 *******************************************************************************/
3078 * @ingroup StrBuf_DeEnCoder
3079 * @brief Wrapper around iconv_open()
3080 * Our version adds aliases for non-standard Microsoft charsets
3081 * such as 'MS950', aliasing them to names like 'CP950'
3083 * @param tocode Target encoding
3084 * @param fromcode Source encoding
3085 * @param pic anonimized pointer to iconv struct
3087 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3090 iconv_t ic = (iconv_t)(-1) ;
3091 ic = iconv_open(tocode, fromcode);
3092 if (ic == (iconv_t)(-1) ) {
3093 char alias_fromcode[64];
3094 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3095 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3096 alias_fromcode[0] = 'C';
3097 alias_fromcode[1] = 'P';
3098 ic = iconv_open(tocode, alias_fromcode);
3101 *(iconv_t *)pic = ic;
3107 * @ingroup StrBuf_DeEnCoder
3108 * @brief find one chunk of a RFC822 encoded string
3109 * @param Buffer where to search
3110 * @param bptr where to start searching
3111 * @returns found position, NULL if none.
3113 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3116 /* Find the next ?Q? */
3117 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3120 end = strchr(bptr + 2, '?');
3125 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3126 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3127 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3128 (*(end + 2) == '?')) {
3129 /* skip on to the end of the cluster, the next ?= */
3130 end = strstr(end + 3, "?=");
3133 /* sort of half valid encoding, try to find an end. */
3134 end = strstr(bptr, "?=");
3141 * @ingroup StrBuf_DeEnCoder
3142 * @brief convert one buffer according to the preselected iconv pointer PIC
3143 * @param ConvertBuf buffer we need to translate
3144 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3145 * @param pic Pointer to the iconv-session Object
3147 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3153 char *ibuf; /**< Buffer of characters to be converted */
3154 char *obuf; /**< Buffer for converted characters */
3155 size_t ibuflen; /**< Length of input buffer */
3156 size_t obuflen; /**< Length of output buffer */
3159 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3162 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3163 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3164 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3166 ic = *(iconv_t*)pic;
3167 ibuf = ConvertBuf->buf;
3168 ibuflen = ConvertBuf->BufUsed;
3170 obuflen = TmpBuf->BufSize;
3172 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3175 if (errno == E2BIG) {
3177 IncreaseBuf(TmpBuf, 0, 0);
3182 else if (errno == EILSEQ){
3183 /* hm, invalid utf8 sequence... what to do now? */
3184 /* An invalid multibyte sequence has been encountered in the input */
3186 else if (errno == EINVAL) {
3187 /* An incomplete multibyte sequence has been encountered in the input. */
3190 FlushStrBuf(TmpBuf);
3193 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3194 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3196 /* little card game: wheres the red lady? */
3197 SwapBuffers(ConvertBuf, TmpBuf);
3198 FlushStrBuf(TmpBuf);
3205 * @ingroup StrBuf_DeEnCoder
3206 * @brief catches one RFC822 encoded segment, and decodes it.
3207 * @param Target buffer to fill with result
3208 * @param DecodeMe buffer with stuff to process
3209 * @param SegmentStart points to our current segment in DecodeMe
3210 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3211 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3212 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3213 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3215 inline static void DecodeSegment(StrBuf *Target,
3216 const StrBuf *DecodeMe,
3217 const char *SegmentStart,
3218 const char *SegmentEnd,
3220 StrBuf *ConvertBuf2,
3221 StrBuf *FoundCharset)
3227 iconv_t ic = (iconv_t)(-1);
3231 /* Now we handle foreign character sets properly encoded
3232 * in RFC2047 format.
3234 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3235 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3236 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3237 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3238 if (FoundCharset != NULL) {
3239 FlushStrBuf(FoundCharset);
3240 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3242 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3243 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3245 *encoding = toupper(*encoding);
3246 if (*encoding == 'B') { /**< base64 */
3247 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3248 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3249 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3251 ConvertBuf->BufUsed);
3253 else if (*encoding == 'Q') { /**< quoted-printable */
3257 while (pos < ConvertBuf->BufUsed)
3259 if (ConvertBuf->buf[pos] == '_')
3260 ConvertBuf->buf[pos] = ' ';
3264 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3265 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3267 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3270 ConvertBuf->BufUsed);
3273 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3276 ctdl_iconv_open("UTF-8", charset, &ic);
3277 if (ic != (iconv_t)(-1) ) {
3279 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3280 StrBufAppendBuf(Target, ConvertBuf2, 0);
3285 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3291 * @ingroup StrBuf_DeEnCoder
3292 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3293 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3294 * @param Target where to put the decoded string to
3295 * @param DecodeMe buffer with encoded string
3296 * @param DefaultCharset if we don't find one, which should we use?
3297 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3298 * put it here for later use where no string might be known.
3300 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3303 StrBuf *ConvertBuf2;
3304 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3305 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3307 StrBuf_RFC822_2_Utf8(Target,
3313 FreeStrBuf(&ConvertBuf);
3314 FreeStrBuf(&ConvertBuf2);
3318 * @ingroup StrBuf_DeEnCoder
3319 * @brief Handle subjects with RFC2047 encoding such as:
3320 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3321 * @param Target where to put the decoded string to
3322 * @param DecodeMe buffer with encoded string
3323 * @param DefaultCharset if we don't find one, which should we use?
3324 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3325 * put it here for later use where no string might be known.
3326 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3327 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3329 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3330 const StrBuf *DecodeMe,
3331 const StrBuf* DefaultCharset,
3332 StrBuf *FoundCharset,
3334 StrBuf *ConvertBuf2)
3336 StrBuf *DecodedInvalidBuf = NULL;
3337 const StrBuf *DecodeMee = DecodeMe;
3338 const char *start, *end, *next, *nextend, *ptr = NULL;
3340 iconv_t ic = (iconv_t)(-1) ;
3345 int illegal_non_rfc2047_encoding = 0;
3348 if (DecodeMe == NULL)
3350 /* Sometimes, badly formed messages contain strings which were simply
3351 * written out directly in some foreign character set instead of
3352 * using RFC2047 encoding. This is illegal but we will attempt to
3353 * handle it anyway by converting from a user-specified default
3354 * charset to UTF-8 if we see any nonprintable characters.
3357 for (i=0; i<DecodeMe->BufUsed; ++i) {
3358 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3359 illegal_non_rfc2047_encoding = 1;
3364 if ((illegal_non_rfc2047_encoding) &&
3365 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3366 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3369 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3370 if (ic != (iconv_t)(-1) ) {
3371 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3372 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3373 DecodeMee = DecodedInvalidBuf;
3379 /* pre evaluate the first pair */
3381 start = strstr(DecodeMee->buf, "=?");
3382 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3384 end = FindNextEnd (DecodeMee, start + 2);
3386 StrBufAppendBuf(Target, DecodeMee, 0);
3387 FreeStrBuf(&DecodedInvalidBuf);
3392 if (start != DecodeMee->buf) {
3395 nFront = start - DecodeMee->buf;
3396 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3399 * Since spammers will go to all sorts of absurd lengths to get their
3400 * messages through, there are LOTS of corrupt headers out there.
3401 * So, prevent a really badly formed RFC2047 header from throwing
3402 * this function into an infinite loop.
3404 while ((start != NULL) &&
3411 DecodeSegment(Target,
3419 next = strstr(end, "=?");
3421 if ((next != NULL) &&
3423 nextend = FindNextEnd(DecodeMee, next);
3424 if (nextend == NULL)
3427 /* did we find two partitions */
3428 if ((next != NULL) &&
3432 while ((ptr < next) &&
3439 * did we find a gab just filled with blanks?
3440 * if not, copy its stuff over.
3444 StrBufAppendBufPlain(Target,
3450 /* our next-pair is our new first pair now. */
3456 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3457 if ((end != NULL) && (end < nextend)) {
3459 while ( (ptr < nextend) &&
3466 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3468 FreeStrBuf(&DecodedInvalidBuf);
3471 /*******************************************************************************
3472 * Manipulating UTF-8 Strings *
3473 *******************************************************************************/
3477 * @brief evaluate the length of an utf8 special character sequence
3478 * @param Char the character to examine
3479 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3481 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3484 unsigned char test = (1<<7);
3486 if ((*CharS & 0xC0) != 0xC0)
3490 ((test & ((unsigned char)*CharS)) != 0))
3495 if ((n > 6) || ((CharE - CharS) < n))
3502 * @brief detect whether this char starts an utf-8 encoded char
3503 * @param Char character to inspect
3504 * @returns yes or no
3506 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3508 /** 11??.???? indicates an UTF8 Sequence. */
3509 return ((Char & 0xC0) == 0xC0);
3514 * @brief measure the number of glyphs in an UTF8 string...
3515 * @param Buf string to measure
3516 * @returns the number of glyphs in Buf
3518 long StrBuf_Utf8StrLen(StrBuf *Buf)
3524 if ((Buf == NULL) || (Buf->BufUsed == 0))
3527 eptr = Buf->buf + Buf->BufUsed;
3528 while ((aptr < eptr) && (*aptr != '\0')) {
3529 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3530 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3531 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3544 * @brief cuts a string after maxlen glyphs
3545 * @param Buf string to cut to maxlen glyphs
3546 * @param maxlen how long may the string become?
3547 * @returns current length of the string
3549 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3555 eptr = Buf->buf + Buf->BufUsed;
3556 while ((aptr < eptr) && (*aptr != '\0')) {
3557 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3558 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3559 while ((*aptr++ != '\0') && (m-- > 0));
3568 Buf->BufUsed = aptr - Buf->buf;
3569 return Buf->BufUsed;
3572 return Buf->BufUsed;
3580 /*******************************************************************************
3582 *******************************************************************************/
3585 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3586 #define OS_CODE 0x03 /*< unix */
3589 * @ingroup StrBuf_DeEnCoder
3590 * @brief uses the same calling syntax as compress2(), but it
3591 * creates a stream compatible with HTTP "Content-encoding: gzip"
3592 * @param dest compressed buffer
3593 * @param destLen length of the compresed data
3594 * @param source source to encode
3595 * @param sourceLen length of source to encode
3596 * @param level compression level
3598 int ZEXPORT compress_gzip(Bytef * dest,
3600 const Bytef * source,
3604 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3606 /* write gzip header */
3607 snprintf((char *) dest, *destLen,
3608 "%c%c%c%c%c%c%c%c%c%c",
3609 gz_magic[0], gz_magic[1], Z_DEFLATED,
3610 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3613 /* normal deflate */
3616 stream.next_in = (Bytef *) source;
3617 stream.avail_in = (uInt) sourceLen;
3618 stream.next_out = dest + 10L; // after header
3619 stream.avail_out = (uInt) * destLen;
3620 if ((uLong) stream.avail_out != *destLen)
3623 stream.zalloc = (alloc_func) 0;
3624 stream.zfree = (free_func) 0;
3625 stream.opaque = (voidpf) 0;
3627 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3628 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3632 err = deflate(&stream, Z_FINISH);
3633 if (err != Z_STREAM_END) {
3634 deflateEnd(&stream);
3635 return err == Z_OK ? Z_BUF_ERROR : err;
3637 *destLen = stream.total_out + 10L;
3639 /* write CRC and Length */
3640 uLong crc = crc32(0L, source, sourceLen);
3642 for (n = 0; n < 4; ++n, ++*destLen) {
3643 dest[*destLen] = (int) (crc & 0xff);
3646 uLong len = stream.total_in;
3647 for (n = 0; n < 4; ++n, ++*destLen) {
3648 dest[*destLen] = (int) (len & 0xff);
3651 err = deflateEnd(&stream);
3658 * @ingroup StrBuf_DeEnCoder
3659 * @brief compress the buffer with gzip
3660 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3661 * @param Buf buffer whose content is to be gzipped
3663 int CompressBuffer(StrBuf *Buf)
3666 char *compressed_data = NULL;
3667 size_t compressed_len, bufsize;
3670 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3671 compressed_data = malloc(compressed_len);
3673 if (compressed_data == NULL)
3675 /* Flush some space after the used payload so valgrind shuts up... */
3676 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3677 Buf->buf[Buf->BufUsed + i++] = '\0';
3678 if (compress_gzip((Bytef *) compressed_data,
3681 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3684 Buf->buf = compressed_data;
3685 Buf->BufUsed = compressed_len;
3686 Buf->BufSize = bufsize;
3687 /* Flush some space after the used payload so valgrind shuts up... */
3689 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3690 Buf->buf[Buf->BufUsed + i++] = '\0';
3693 free(compressed_data);
3695 #endif /* HAVE_ZLIB */
3699 /*******************************************************************************
3700 * File I/O; Callbacks to libevent *
3701 *******************************************************************************/
3703 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3708 if ((FB == NULL) || (FB->Buf == NULL))
3712 * check whether the read pointer is somewhere in a range
3713 * where a cut left is inexpensive
3716 if (FB->ReadWritePointer != NULL)
3720 already_read = FB->ReadWritePointer - FB->Buf->buf;
3721 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3723 if (already_read != 0) {
3726 unread = FB->Buf->BufUsed - already_read;
3728 /* else nothing to compact... */
3730 FB->ReadWritePointer = FB->Buf->buf;
3731 bufremain = FB->Buf->BufSize;
3733 else if ((unread < 64) ||
3734 (bufremain < already_read))
3737 * if its just a tiny bit remaining, or we run out of space...
3740 FB->Buf->BufUsed = unread;
3741 if (unread < already_read)
3742 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3744 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3745 FB->ReadWritePointer = FB->Buf->buf;
3746 bufremain = FB->Buf->BufSize - unread - 1;
3748 else if (bufremain < (FB->Buf->BufSize / 10))
3750 /* get a bigger buffer */
3752 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3754 FB->ReadWritePointer = FB->Buf->buf + unread;
3756 bufremain = FB->Buf->BufSize - unread - 1;
3757 /*TODO: special increase function that won't copy the already read! */
3760 else if (bufremain < 10) {
3761 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3763 FB->ReadWritePointer = FB->Buf->buf;
3765 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3770 FB->ReadWritePointer = FB->Buf->buf;
3771 bufremain = FB->Buf->BufSize - 1;
3774 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3777 FB->Buf->BufUsed += n;
3778 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3783 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3788 if ((FB == NULL) || (FB->Buf == NULL))
3791 if (FB->ReadWritePointer != NULL)
3793 WriteRemain = FB->Buf->BufUsed -
3794 (FB->ReadWritePointer -
3798 FB->ReadWritePointer = FB->Buf->buf;
3799 WriteRemain = FB->Buf->BufUsed;
3802 n = write(fd, FB->ReadWritePointer, WriteRemain);
3804 FB->ReadWritePointer += n;
3806 if (FB->ReadWritePointer ==
3807 FB->Buf->buf + FB->Buf->BufUsed)
3809 FlushStrBuf(FB->Buf);
3810 FB->ReadWritePointer = NULL;
3813 // check whether we've got something to write
3814 // get the maximum chunk plus the pointer we can send
3815 // write whats there
3816 // if not all was sent, remember the send pointer for the next time
3817 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3823 * @ingroup StrBuf_IO
3824 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3825 * @param LineBuf your line will be copied here.
3826 * @param FB BLOB with lines of text...
3827 * @param Ptr moved arround to keep the next-line across several iterations
3828 * has to be &NULL on start; will be &NotNULL on end of buffer
3829 * @returns size of copied buffer
3831 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3833 const char *aptr, *ptr, *eptr;
3836 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3840 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3841 FB->ReadWritePointer = StrBufNOTNULL;
3845 FlushStrBuf(LineBuf);
3846 if (FB->ReadWritePointer == NULL)
3847 ptr = aptr = FB->Buf->buf;
3849 ptr = aptr = FB->ReadWritePointer;
3851 optr = LineBuf->buf;
3852 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3853 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3855 while ((ptr <= eptr) &&
3862 LineBuf->BufUsed = optr - LineBuf->buf;
3863 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3864 optr = LineBuf->buf + LineBuf->BufUsed;
3865 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3870 if (optr > LineBuf->buf)
3872 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3873 LineBuf->BufUsed = optr - LineBuf->buf;
3875 if ((FB->ReadWritePointer != NULL) &&
3876 (FB->ReadWritePointer != FB->Buf->buf))
3878 /* Ok, the client application read all the data
3879 it was interested in so far. Since there is more to read,
3880 we now shrink the buffer, and move the rest over.
3882 StrBufCutLeft(FB->Buf,
3883 FB->ReadWritePointer - FB->Buf->buf);
3884 FB->ReadWritePointer = FB->Buf->buf;
3886 return eMustReadMore;
3889 LineBuf->BufUsed = optr - LineBuf->buf;
3891 if ((ptr <= eptr) && (*ptr == '\r'))
3893 if ((ptr <= eptr) && (*ptr == '\n'))
3897 FB->ReadWritePointer = ptr;
3900 FlushStrBuf(FB->Buf);
3901 FB->ReadWritePointer = NULL;
3904 return eReadSuccess;
3908 * @ingroup StrBuf_CHUNKED_IO
3909 * @brief check whether the chunk-buffer has more data waiting or not.
3910 * @param FB Chunk-Buffer to inspect
3912 eReadState StrBufCheckBuffer(IOBuffer *FB)
3916 if (FB->Buf->BufUsed == 0)
3917 return eReadSuccess;
3918 if (FB->ReadWritePointer == NULL)
3919 return eBufferNotEmpty;
3920 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3921 return eBufferNotEmpty;
3922 return eReadSuccess;
3925 long IOBufferStrLength(IOBuffer *FB)
3927 if ((FB == NULL) || (FB->Buf == NULL))
3929 if (FB->ReadWritePointer == NULL)
3930 return StrLength(FB->Buf);
3932 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3935 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3937 memset(FDB, 0, sizeof(FDIOBuffer));
3939 FDB->TotalSendSize = TotalSendSize;
3943 pipe(FDB->SplicePipe);
3946 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3951 void FDIOBufferDelete(FDIOBuffer *FDB)
3956 close(FDB->SplicePipe[0]);
3957 close(FDB->SplicePipe[1]);
3961 FreeStrBuf(&FDB->ChunkBuffer);
3963 close(FDB->OtherFD);
3964 memset(FDB, 0, sizeof(FDIOBuffer));
3967 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3969 ssize_t sent, pipesize;
3973 if (FDB->PipeSize == 0)
3975 pipesize = splice(FDB->OtherFD,
3976 &FDB->TotalSentAlready,
3979 FDB->ChunkSendRemain,
3984 *Err = strerror(errno);
3987 FDB->PipeSize = pipesize;
3989 sent = splice(FDB->SplicePipe[0],
3994 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
3997 *Err = strerror(errno);
4000 FDB->PipeSize -= sent;
4001 FDB->ChunkSendRemain -= sent;
4010 pRead = FDB->ChunkBuffer->buf;
4011 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4013 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4015 FDB->ChunkBuffer->BufUsed += nRead;
4016 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4018 else if (nRead == 0) {}
4023 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
4026 FDB->TotalSentAlready += nRead;
4027 FDB->ChunkSendRemain -= nRead;
4028 return FDB->ChunkSendRemain;
4036 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4038 ssize_t sent, pipesize;
4043 if (FDB->PipeSize == 0)
4045 pipesize = splice(FDB->IOB->fd,
4049 FDB->ChunkSendRemain,
4050 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4054 *Err = strerror(errno);
4057 FDB->PipeSize = pipesize;
4060 sent = splice(FDB->SplicePipe[0],
4063 &FDB->TotalSentAlready,
4065 SPLICE_F_MORE | SPLICE_F_MOVE);
4069 *Err = strerror(errno);
4072 FDB->PipeSize -= sent;
4073 FDB->ChunkSendRemain -= sent;
4079 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4084 FDB->ChunkBuffer->BufUsed = sent;
4086 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4087 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4089 *Err = strerror(errno);
4095 FDB->ChunkBuffer->BufUsed = 0;
4096 FDB->TotalSentAlready += sent;
4097 FDB->ChunkSendRemain -= sent;
4098 return FDB->ChunkSendRemain;
4100 else if (sent < 0) {
4101 *Err = strerror(errno);
4108 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4110 ssize_t sent, pipesize;
4115 if (FDB->PipeSize == 0)
4117 pipesize = splice(FDB->IOB->fd,
4118 &FDB->TotalReadAlready,
4121 FDB->ChunkSendRemain,
4122 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4126 *Err = strerror(errno);
4129 FDB->PipeSize = pipesize;
4132 sent = splice(FDB->SplicePipe[0],
4135 &FDB->TotalSentAlready,
4137 SPLICE_F_MORE | SPLICE_F_MOVE);
4141 *Err = strerror(errno);
4144 FDB->PipeSize -= sent;
4145 FDB->ChunkSendRemain -= sent;
4151 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4156 FDB->ChunkBuffer->BufUsed = sent;
4158 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4159 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4161 *Err = strerror(errno);
4167 FDB->ChunkBuffer->BufUsed = 0;
4168 FDB->TotalSentAlready += sent;
4169 FDB->ChunkSendRemain -= sent;
4170 return FDB->ChunkSendRemain;
4172 else if (sent < 0) {
4173 *Err = strerror(errno);
4180 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4186 int nSuccessLess = 0;
4190 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4191 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4193 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4194 (FDB->ChunkSendRemain > 0))
4197 tv.tv_sec = 1; /* selectresolution; */
4201 FD_SET(FDB->OtherFD, &rfds);
4202 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4203 *Error = strerror(errno);
4207 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4212 should_write = FDB->IOB->Buf->BufUsed -
4213 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4214 if (should_write > FDB->ChunkSendRemain)
4215 should_write = FDB->ChunkSendRemain;
4217 rlen = write(FDB->OtherFD,
4218 FDB->IOB->ReadWritePointer,
4221 *Error = strerror(errno);
4225 FDB->TotalSentAlready += rlen;
4226 FDB->IOB->ReadWritePointer += rlen;
4227 FDB->ChunkSendRemain -= rlen;
4229 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4231 FlushStrBuf(FDB->IOB->Buf);
4232 FDB->IOB->ReadWritePointer = NULL;
4235 if (FDB->ChunkSendRemain == 0)
4236 return eReadSuccess;
4238 return eMustReadMore;
4241 /*******************************************************************************
4242 * File I/O; Prefer buffered read since its faster! *
4243 *******************************************************************************/
4246 * @ingroup StrBuf_IO
4247 * @brief Read a line from socket
4248 * flushes and closes the FD on error
4249 * @param buf the buffer to get the input to
4250 * @param fd pointer to the filedescriptor to read
4251 * @param append Append to an existing string or replace?
4252 * @param Error strerror() on error
4253 * @returns numbers of chars read
4255 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4257 int len, rlen, slen;
4259 if ((buf == NULL) || (buf->buf == NULL)) {
4260 *Error = strerror(EINVAL);
4267 slen = len = buf->BufUsed;
4269 rlen = read(*fd, &buf->buf[len], 1);
4271 *Error = strerror(errno);
4278 if (buf->buf[len] == '\n')
4280 if (buf->buf[len] != '\r')
4282 if (len + 2 >= buf->BufSize) {
4284 buf->buf[len+1] = '\0';
4285 IncreaseBuf(buf, 1, -1);
4289 buf->buf[len] = '\0';
4294 * @ingroup StrBuf_BufferedIO
4295 * @brief Read a line from socket
4296 * flushes and closes the FD on error
4297 * @param Line the line to read from the fd / I/O Buffer
4298 * @param buf the buffer to get the input to
4299 * @param fd pointer to the filedescriptor to read
4300 * @param timeout number of successless selects until we bail out
4301 * @param selectresolution how long to wait on each select
4302 * @param Error strerror() on error
4303 * @returns numbers of chars read
4305 int StrBufTCP_read_buffered_line(StrBuf *Line,
4309 int selectresolution,
4313 int nSuccessLess = 0;
4320 if (buf->BufUsed > 0) {
4321 pch = strchr(buf->buf, '\n');
4324 len = pch - buf->buf;
4325 if (len > 0 && (*(pch - 1) == '\r') )
4327 StrBufSub(Line, buf, 0, len - rlen);
4328 StrBufCutLeft(buf, len + 1);
4333 if (buf->BufSize - buf->BufUsed < 10)
4334 IncreaseBuf(buf, 1, -1);
4336 fdflags = fcntl(*fd, F_GETFL);
4337 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4339 while ((nSuccessLess < timeout) && (pch == NULL)) {
4341 tv.tv_sec = selectresolution;
4346 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4347 *Error = strerror(errno);
4353 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4358 &buf->buf[buf->BufUsed],
4359 buf->BufSize - buf->BufUsed - 1);
4361 *Error = strerror(errno);
4366 else if (rlen > 0) {
4368 buf->BufUsed += rlen;
4369 buf->buf[buf->BufUsed] = '\0';
4370 pch = strchr(buf->buf, '\n');
4371 if ((pch == NULL) &&
4372 (buf->BufUsed + 10 > buf->BufSize) &&
4373 (IncreaseBuf(buf, 1, -1) == -1))
4381 len = pch - buf->buf;
4382 if (len > 0 && (*(pch - 1) == '\r') )
4384 StrBufSub(Line, buf, 0, len - rlen);
4385 StrBufCutLeft(buf, len + 1);
4392 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4393 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4394 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4396 * @ingroup StrBuf_BufferedIO
4397 * @brief Read a line from socket
4398 * flushes and closes the FD on error
4399 * @param Line where to append our Line read from the fd / I/O Buffer;
4400 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4401 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4402 * @param fd pointer to the filedescriptor to read
4403 * @param timeout number of successless selects until we bail out
4404 * @param selectresolution how long to wait on each select
4405 * @param Error strerror() on error
4406 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4408 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4413 int selectresolution,
4416 const char *pche = NULL;
4417 const char *pos = NULL;
4419 int len, rlen, retlen;
4420 int nSuccessLess = 0;
4422 const char *pch = NULL;
4428 if ((Line == NULL) ||
4435 *Error = ErrRBLF_PreConditionFailed;
4440 if ((IOBuf->BufUsed > 0) &&
4442 (pos < IOBuf->buf + IOBuf->BufUsed))
4446 pche = IOBuf->buf + IOBuf->BufUsed;
4450 while ((pch < pche) && (*pch != '\n'))
4452 if (Line->BufUsed + 10 > Line->BufSize)
4455 apos = pcht - Line->buf;
4457 IncreaseBuf(Line, 1, -1);
4458 pcht = Line->buf + apos;
4466 if (len > 0 && (*(pch - 1) == '\r') )
4475 if ((pch >= pche) || (*pch == '\0'))
4483 if ((pch != NULL) &&
4486 if (pch + 1 >= pche) {
4499 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4501 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4502 IncreaseBuf(IOBuf, 1, -1);
4504 fdflags = fcntl(*fd, F_GETFL);
4505 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4508 while ((nSuccessLess < timeout) &&
4518 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4519 *Error = strerror(errno);
4523 *Error = ErrRBLF_SelectFailed;
4526 if (! FD_ISSET(*fd, &rfds) != 0) {
4532 &IOBuf->buf[IOBuf->BufUsed],
4533 IOBuf->BufSize - IOBuf->BufUsed - 1);
4535 *Error = strerror(errno);
4540 else if (rlen > 0) {
4542 pLF = IOBuf->buf + IOBuf->BufUsed;
4543 IOBuf->BufUsed += rlen;
4544 IOBuf->buf[IOBuf->BufUsed] = '\0';
4546 pche = IOBuf->buf + IOBuf->BufUsed;
4548 while ((pLF < pche) && (*pLF != '\n'))
4550 if ((pLF >= pche) || (*pLF == '\0'))
4553 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4557 if (pLF != NULL) apos = pLF - IOBuf->buf;
4558 IncreaseBuf(IOBuf, 1, -1);
4559 if (pLF != NULL) pLF = IOBuf->buf + apos;
4573 if (len > 0 && (*(pLF - 1) == '\r') )
4575 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4576 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4582 return retlen + len;
4584 *Error = ErrRBLF_NotEnoughSentFromServer;
4589 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4591 * @ingroup StrBuf_IO
4592 * @brief Input binary data from socket
4593 * flushes and closes the FD on error
4594 * @param Buf the buffer to get the input to
4595 * @param fd pointer to the filedescriptor to read
4596 * @param append Append to an existing string or replace?
4597 * @param nBytes the maximal number of bytes to read
4598 * @param Error strerror() on error
4599 * @returns numbers of chars read
4601 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4612 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4614 *Error = ErrRBLF_BLOBPreConditionFailed;
4619 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4620 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4622 ptr = Buf->buf + Buf->BufUsed;
4624 fdflags = fcntl(*fd, F_GETFL);
4625 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4627 while ((nRead < nBytes) &&
4637 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4638 *Error = strerror(errno);
4642 *Error = ErrRBLF_SelectFailed;
4645 if (! FD_ISSET(*fd, &rfds) != 0) {
4651 if ((rlen = read(*fd,
4653 nBytes - nRead)) == -1) {
4656 *Error = strerror(errno);
4661 Buf->BufUsed += rlen;
4663 Buf->buf[Buf->BufUsed] = '\0';
4667 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4668 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4670 * @ingroup StrBuf_BufferedIO
4671 * @brief Input binary data from socket
4672 * flushes and closes the FD on error
4673 * @param Blob put binary thing here
4674 * @param IOBuf the buffer to get the input to
4675 * @param Pos offset inside of IOBuf
4676 * @param fd pointer to the filedescriptor to read
4677 * @param append Append to an existing string or replace?
4678 * @param nBytes the maximal number of bytes to read
4679 * @param check whether we should search for '000\n' terminators in case of timeouts
4680 * @param Error strerror() on error
4681 * @returns numbers of chars read
4683 int StrBufReadBLOBBuffered(StrBuf *Blob,
4696 int nAlreadyRead = 0;
4701 int nSuccessLess = 0;
4704 if ((Blob == NULL) ||
4711 *Error = ErrRBB_BLOBFPreConditionFailed;
4717 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4718 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4723 rlen = pos - IOBuf->buf;
4724 rlen = IOBuf->BufUsed - rlen;
4727 if ((IOBuf->BufUsed > 0) &&
4729 (pos < IOBuf->buf + IOBuf->BufUsed))
4731 if (rlen < nBytes) {
4732 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4733 Blob->BufUsed += rlen;
4734 Blob->buf[Blob->BufUsed] = '\0';
4735 nAlreadyRead = nRead = rlen;
4738 if (rlen >= nBytes) {
4739 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4740 Blob->BufUsed += nBytes;
4741 Blob->buf[Blob->BufUsed] = '\0';
4742 if (rlen == nBytes) {
4754 if (IOBuf->BufSize < nBytes - nRead)
4755 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4758 fdflags = fcntl(*fd, F_GETFL);
4759 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4767 while ((nSuccessLess < MaxTries) &&
4777 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4778 *Error = strerror(errno);
4782 *Error = ErrRBLF_SelectFailed;
4785 if (! FD_ISSET(*fd, &rfds) != 0) {
4792 IOBuf->BufSize - (ptr - IOBuf->buf));
4796 *Error = strerror(errno);
4799 else if (rlen == 0){
4800 if ((check == NNN_TERM) &&
4802 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4804 StrBufPlain(Blob, HKEY("\n000\n"));
4805 StrBufCutRight(Blob, 5);
4806 return Blob->BufUsed;
4808 else if (!IsNonBlock)
4810 else if (nSuccessLess > MaxTries) {
4812 *Error = ErrRBB_too_many_selects;
4816 else if (rlen > 0) {
4820 IOBuf->BufUsed += rlen;
4823 if (nSuccessLess >= MaxTries) {
4825 *Error = ErrRBB_too_many_selects;
4829 if (nRead > nBytes) {
4830 *Pos = IOBuf->buf + nBytes;
4832 Blob->buf[Blob->BufUsed] = '\0';
4833 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4837 return nRead + nAlreadyRead;
4841 * @ingroup StrBuf_IO
4842 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4843 * @param LineBuf your line will be copied here.
4844 * @param Buf BLOB with lines of text...
4845 * @param Ptr moved arround to keep the next-line across several iterations
4846 * has to be &NULL on start; will be &NotNULL on end of buffer
4847 * @returns size of remaining buffer
4849 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4851 const char *aptr, *ptr, *eptr;
4854 if ((Buf == NULL) ||
4855 (*Ptr == StrBufNOTNULL) ||
4857 (LineBuf->buf == NULL))
4859 *Ptr = StrBufNOTNULL;
4863 FlushStrBuf(LineBuf);
4865 ptr = aptr = Buf->buf;
4869 optr = LineBuf->buf;
4870 eptr = Buf->buf + Buf->BufUsed;
4871 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4873 while ((ptr <= eptr) &&
4880 LineBuf->BufUsed = optr - LineBuf->buf;
4881 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4882 optr = LineBuf->buf + LineBuf->BufUsed;
4883 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4887 if ((ptr >= eptr) && (optr > LineBuf->buf))
4889 LineBuf->BufUsed = optr - LineBuf->buf;
4891 if ((ptr <= eptr) && (*ptr == '\r'))
4893 if ((ptr <= eptr) && (*ptr == '\n'))
4900 *Ptr = StrBufNOTNULL;
4903 return Buf->BufUsed - (ptr - Buf->buf);
4908 * @ingroup StrBuf_IO
4909 * @brief removes double slashes from pathnames
4910 * @param Dir directory string to filter
4911 * @param RemoveTrailingSlash allows / disallows trailing slashes
4913 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4919 while (!IsEmptyStr(a)) {
4931 if ((RemoveTrailingSlash) &&
4937 Dir->BufUsed = b - Dir->buf;