2 * Copyright (c) 1987-2013 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)
1179 if ((Buf == NULL) || (Buf->buf == NULL)) {
1183 pRight = strchr(Buf->buf, rightboundary);
1184 if (pRight != NULL) {
1185 StrBufCutAt(Buf, 0, pRight);
1188 StrBufCutAt(Buf, 0, Buf->buf);
1192 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1193 if (pLeft != NULL) {
1194 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1197 StrBufCutAt(Buf, 0, Buf->buf);
1204 * @ingroup StrBuf_Filler
1205 * @brief uppercase the contents of a buffer
1206 * @param Buf the buffer to translate
1208 void StrBufUpCase(StrBuf *Buf)
1212 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1215 pche = pch + Buf->BufUsed;
1216 while (pch < pche) {
1217 *pch = toupper(*pch);
1224 * @ingroup StrBuf_Filler
1225 * @brief lowercase the contents of a buffer
1226 * @param Buf the buffer to translate
1228 void StrBufLowerCase(StrBuf *Buf)
1232 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1235 pche = pch + Buf->BufUsed;
1236 while (pch < pche) {
1237 *pch = tolower(*pch);
1243 /*******************************************************************************
1244 * a tokenizer that kills, maims, and destroys *
1245 *******************************************************************************/
1248 * @ingroup StrBuf_Tokenizer
1249 * @brief Replace a token at a given place with a given length by another token with given length
1250 * @param Buf String where to work on
1251 * @param where where inside of the Buf is the search-token
1252 * @param HowLong How long is the token to be replaced
1253 * @param Repl Token to insert at 'where'
1254 * @param ReplLen Length of repl
1255 * @returns -1 if fail else length of resulting Buf
1257 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1258 const char *Repl, long ReplLen)
1261 if ((Buf == NULL) ||
1262 (where > Buf->BufUsed) ||
1263 (where + HowLong > Buf->BufUsed))
1266 if (where + ReplLen - HowLong > Buf->BufSize)
1267 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1270 memmove(Buf->buf + where + ReplLen,
1271 Buf->buf + where + HowLong,
1272 Buf->BufUsed - where - HowLong);
1274 memcpy(Buf->buf + where,
1277 Buf->BufUsed += ReplLen - HowLong;
1279 return Buf->BufUsed;
1283 * @ingroup StrBuf_Tokenizer
1284 * @brief Counts the numbmer of tokens in a buffer
1285 * @param source String to count tokens in
1286 * @param tok Tokenizer char to count
1287 * @returns numbers of tokenizer chars found
1289 int StrBufNum_tokens(const StrBuf *source, char tok)
1293 if ((source == NULL) || (source->BufUsed == 0))
1295 if ((source->BufUsed == 1) && (*source->buf == tok))
1299 pche = pch + source->BufUsed;
1310 * @ingroup StrBuf_Tokenizer
1311 * @brief a string tokenizer
1312 * @param Source StringBuffer to read into
1313 * @param parmnum n'th Parameter to remove
1314 * @param separator tokenizer character
1315 * @returns -1 if not found, else length of token.
1317 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1320 char *d, *s, *end; /* dest, source */
1323 /* Find desired @parameter */
1324 end = Source->buf + Source->BufUsed;
1326 while ((d <= end) &&
1329 /* End of string, bail! */
1334 if (*d == separator) {
1339 if ((d == NULL) || (d >= end))
1340 return 0; /* @Parameter not found */
1342 /* Find next @parameter */
1344 while ((s <= end) &&
1345 (*s && *s != separator))
1349 if (*s == separator)
1353 /* Hack and slash */
1358 memmove(d, s, Source->BufUsed - (s - Source->buf));
1359 Source->BufUsed += ReducedBy;
1360 Source->buf[Source->BufUsed] = '\0';
1362 else if (d == Source->buf) {
1364 Source->BufUsed = 0;
1368 Source->BufUsed += ReducedBy;
1379 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1381 const StrBuf Temp = {
1394 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1398 * @ingroup StrBuf_Tokenizer
1399 * @brief a string tokenizer
1400 * @param dest Destination StringBuffer
1401 * @param Source StringBuffer to read into
1402 * @param parmnum n'th Parameter to extract
1403 * @param separator tokenizer character
1404 * @returns -1 if not found, else length of token.
1406 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1408 const char *s, *e; //* source * /
1409 int len = 0; //* running total length of extracted string * /
1410 int current_token = 0; //* token currently being processed * /
1413 dest->buf[0] = '\0';
1419 if ((Source == NULL) || (Source->BufUsed ==0)) {
1423 e = s + Source->BufUsed;
1426 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1428 while ((s < e) && !IsEmptyStr(s)) {
1429 if (*s == separator) {
1432 if (len >= dest->BufSize) {
1433 dest->BufUsed = len;
1434 if (IncreaseBuf(dest, 1, -1) < 0) {
1439 if ( (current_token == parmnum) &&
1440 (*s != separator)) {
1441 dest->buf[len] = *s;
1444 else if (current_token > parmnum) {
1450 dest->buf[len] = '\0';
1451 dest->BufUsed = len;
1453 if (current_token < parmnum) {
1454 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1457 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1466 * @ingroup StrBuf_Tokenizer
1467 * @brief a string tokenizer to fetch an integer
1468 * @param Source String containing tokens
1469 * @param parmnum n'th Parameter to extract
1470 * @param separator tokenizer character
1471 * @returns 0 if not found, else integer representation of the token
1473 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1483 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1490 * @ingroup StrBuf_Tokenizer
1491 * @brief a string tokenizer to fetch a long integer
1492 * @param Source String containing tokens
1493 * @param parmnum n'th Parameter to extract
1494 * @param separator tokenizer character
1495 * @returns 0 if not found, else long integer representation of the token
1497 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1507 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1515 * @ingroup StrBuf_Tokenizer
1516 * @brief a string tokenizer to fetch an unsigned long
1517 * @param Source String containing tokens
1518 * @param parmnum n'th Parameter to extract
1519 * @param separator tokenizer character
1520 * @returns 0 if not found, else unsigned long representation of the token
1522 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1533 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1537 return (unsigned long) atol(pnum);
1546 * @ingroup StrBuf_NextTokenizer
1547 * @brief a string tokenizer; Bounds checker
1548 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1549 * @param Source our tokenbuffer
1550 * @param pStart the token iterator pointer to inspect
1551 * @returns whether the revolving pointer is inside of the search range
1553 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1555 if ((Source == NULL) ||
1556 (*pStart == StrBufNOTNULL) ||
1557 (Source->BufUsed == 0))
1561 if (*pStart == NULL)
1565 else if (*pStart > Source->buf + Source->BufUsed)
1569 else if (*pStart <= Source->buf)
1578 * @ingroup StrBuf_NextTokenizer
1579 * @brief a string tokenizer
1580 * @param dest Destination StringBuffer
1581 * @param Source StringBuffer to read into
1582 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1583 * @param separator tokenizer
1584 * @returns -1 if not found, else length of token.
1586 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1588 const char *s; /* source */
1589 const char *EndBuffer; /* end stop of source buffer */
1590 int current_token = 0; /* token currently being processed */
1591 int len = 0; /* running total length of extracted string */
1593 if ((Source == NULL) ||
1594 (Source->BufUsed == 0) )
1596 *pStart = StrBufNOTNULL;
1602 EndBuffer = Source->buf + Source->BufUsed;
1606 dest->buf[0] = '\0';
1611 *pStart = EndBuffer + 1;
1615 if (*pStart == NULL)
1617 *pStart = Source->buf; /* we're starting to examine this buffer. */
1619 else if ((*pStart < Source->buf) ||
1620 (*pStart > EndBuffer ) )
1622 return -1; /* no more tokens to find. */
1626 /* start to find the next token */
1627 while ((s <= EndBuffer) &&
1628 (current_token == 0) )
1630 if (*s == separator)
1632 /* we found the next token */
1636 if (len >= dest->BufSize)
1638 /* our Dest-buffer isn't big enough, increase it. */
1639 dest->BufUsed = len;
1641 if (IncreaseBuf(dest, 1, -1) < 0) {
1642 /* WHUT? no more mem? bail out. */
1649 if ( (current_token == 0 ) && /* are we in our target token? */
1650 (!IsEmptyStr(s) ) &&
1651 (separator != *s) ) /* don't copy the token itself */
1653 dest->buf[len] = *s; /* Copy the payload */
1654 ++len; /* remember the bigger size. */
1660 /* did we reach the end? */
1661 if ((s > EndBuffer)) {
1662 EndBuffer = StrBufNOTNULL;
1663 *pStart = EndBuffer;
1666 *pStart = s; /* remember the position for the next run */
1669 /* sanitize our extracted token */
1670 dest->buf[len] = '\0';
1671 dest->BufUsed = len;
1678 * @ingroup StrBuf_NextTokenizer
1679 * @brief a string tokenizer
1680 * @param Source StringBuffer to read from
1681 * @param pStart pointer to the end of the last token. Feed with NULL.
1682 * @param separator tokenizer character
1683 * @param nTokens number of tokens to fastforward over
1684 * @returns -1 if not found, else length of token.
1686 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1688 const char *s, *EndBuffer; //* source * /
1689 int len = 0; //* running total length of extracted string * /
1690 int current_token = 0; //* token currently being processed * /
1692 if ((Source == NULL) ||
1693 (Source->BufUsed ==0)) {
1697 return Source->BufUsed;
1699 if (*pStart == NULL)
1700 *pStart = Source->buf;
1702 EndBuffer = Source->buf + Source->BufUsed;
1704 if ((*pStart < Source->buf) ||
1705 (*pStart > EndBuffer)) {
1713 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1715 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1716 if (*s == separator) {
1719 if (current_token >= nTokens) {
1731 * @ingroup StrBuf_NextTokenizer
1732 * @brief a string tokenizer to fetch an integer
1733 * @param Source StringBuffer to read from
1734 * @param pStart Cursor on the tokenstring
1735 * @param separator tokenizer character
1736 * @returns 0 if not found, else integer representation of the token
1738 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1748 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1755 * @ingroup StrBuf_NextTokenizer
1756 * @brief a string tokenizer to fetch a long integer
1757 * @param Source StringBuffer to read from
1758 * @param pStart Cursor on the tokenstring
1759 * @param separator tokenizer character
1760 * @returns 0 if not found, else long integer representation of the token
1762 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1772 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1780 * @ingroup StrBuf_NextTokenizer
1781 * @brief a string tokenizer to fetch an unsigned long
1782 * @param Source StringBuffer to read from
1783 * @param pStart Cursor on the tokenstring
1784 * @param separator tokenizer character
1785 * @returns 0 if not found, else unsigned long representation of the token
1787 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1798 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1802 return (unsigned long) atol(pnum);
1812 /*******************************************************************************
1813 * Escape Appending *
1814 *******************************************************************************/
1817 * @ingroup StrBuf_DeEnCoder
1818 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1819 * @param OutBuf the output buffer
1820 * @param In Buffer to encode
1821 * @param PlainIn way in from plain old c strings
1823 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1825 const char *pch, *pche;
1829 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1831 if (PlainIn != NULL) {
1832 len = strlen(PlainIn);
1838 pche = pch + In->BufUsed;
1845 pt = OutBuf->buf + OutBuf->BufUsed;
1846 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1848 while (pch < pche) {
1850 IncreaseBuf(OutBuf, 1, -1);
1851 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1852 pt = OutBuf->buf + OutBuf->BufUsed;
1855 if((*pch >= 'a' && *pch <= 'z') ||
1856 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1857 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1858 (*pch == '!') || (*pch == '_') ||
1859 (*pch == ',') || (*pch == '.'))
1866 *(pt + 1) = HexList[(unsigned char)*pch][0];
1867 *(pt + 2) = HexList[(unsigned char)*pch][1];
1869 OutBuf->BufUsed += 3;
1877 * @ingroup StrBuf_DeEnCoder
1878 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1879 * @param OutBuf the output buffer
1880 * @param In Buffer to encode
1881 * @param PlainIn way in from plain old c strings
1883 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1885 const char *pch, *pche;
1889 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1891 if (PlainIn != NULL) {
1892 len = strlen(PlainIn);
1898 pche = pch + In->BufUsed;
1905 pt = OutBuf->buf + OutBuf->BufUsed;
1906 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1908 while (pch < pche) {
1910 IncreaseBuf(OutBuf, 1, -1);
1911 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1912 pt = OutBuf->buf + OutBuf->BufUsed;
1915 if((*pch >= 'a' && *pch <= 'z') ||
1916 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1917 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1918 (*pch == '!') || (*pch == '_') ||
1919 (*pch == ',') || (*pch == '.'))
1926 *(pt + 1) = HexList[(unsigned char)*pch][0];
1927 *(pt + 2) = HexList[(unsigned char)*pch][1];
1929 OutBuf->BufUsed += 3;
1937 * @ingroup StrBuf_DeEnCoder
1938 * @brief append a string in hex encoding to the buffer
1939 * @param OutBuf the output buffer
1940 * @param In Buffer to encode
1941 * @param PlainIn way in from plain old c strings
1942 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1944 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1946 const unsigned char *pch, *pche;
1950 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1952 if (PlainIn != NULL) {
1954 len = strlen((const char*)PlainIn);
1961 pch = (const unsigned char*)In->buf;
1962 pche = pch + In->BufUsed;
1969 pt = OutBuf->buf + OutBuf->BufUsed;
1970 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1972 while (pch < pche) {
1974 IncreaseBuf(OutBuf, 1, -1);
1975 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1976 pt = OutBuf->buf + OutBuf->BufUsed;
1979 *pt = HexList[*pch][0];
1981 *pt = HexList[*pch][1];
1982 pt ++; pch ++; OutBuf->BufUsed += 2;
1987 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
1994 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1996 if (PlainIn != NULL) {
1998 len = strlen(PlainIn);
2011 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2013 if (ExpectLen > OutBuf->BufSize)
2014 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2017 pt = OutBuf->buf + OutBuf->BufUsed;
2019 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2022 OutBuf->BufUsed += len;
2027 * @ingroup StrBuf_DeEnCoder
2028 * @brief append a string in hex encoding to the buffer
2029 * @param OutBuf the output buffer
2030 * @param In Buffer to encode
2031 * @param PlainIn way in from plain old c strings
2033 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2035 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2039 * @ingroup StrBuf_DeEnCoder
2040 * @brief Append a string, escaping characters which have meaning in HTML.
2042 * @param Target target buffer
2043 * @param Source source buffer; set to NULL if you just have a C-String
2044 * @param PlainIn Plain-C string to append; set to NULL if unused
2045 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2046 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2047 * if set to 2, linebreaks are replaced by <br/>
2049 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2051 const char *aptr, *eiptr;
2055 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2058 if (PlainIn != NULL) {
2060 len = strlen(PlainIn);
2065 eiptr = aptr + Source->BufUsed;
2066 len = Source->BufUsed;
2072 bptr = Target->buf + Target->BufUsed;
2073 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2075 while (aptr < eiptr){
2077 IncreaseBuf(Target, 1, -1);
2078 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2079 bptr = Target->buf + Target->BufUsed;
2082 memcpy(bptr, "<", 4);
2084 Target->BufUsed += 4;
2086 else if (*aptr == '>') {
2087 memcpy(bptr, ">", 4);
2089 Target->BufUsed += 4;
2091 else if (*aptr == '&') {
2092 memcpy(bptr, "&", 5);
2094 Target->BufUsed += 5;
2096 else if (*aptr == '"') {
2097 memcpy(bptr, """, 6);
2099 Target->BufUsed += 6;
2101 else if (*aptr == '\'') {
2102 memcpy(bptr, "'", 5);
2104 Target->BufUsed += 5;
2106 else if (*aptr == LB) {
2111 else if (*aptr == RB) {
2116 else if (*aptr == QU) {
2121 else if ((*aptr == 32) && (nbsp == 1)) {
2122 memcpy(bptr, " ", 6);
2124 Target->BufUsed += 6;
2126 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2127 *bptr='\0'; /* nothing */
2129 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2130 memcpy(bptr, "<br/>", 11);
2132 Target->BufUsed += 11;
2136 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2137 *bptr='\0'; /* nothing */
2147 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2149 return Target->BufUsed;
2153 * @ingroup StrBuf_DeEnCoder
2154 * @brief Append a string, escaping characters which have meaning in HTML.
2155 * Converts linebreaks into blanks; escapes single quotes
2156 * @param Target target buffer
2157 * @param Source source buffer; set to NULL if you just have a C-String
2158 * @param PlainIn Plain-C string to append; set to NULL if unused
2160 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2162 const char *aptr, *eiptr;
2166 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2169 if (PlainIn != NULL) {
2171 len = strlen(PlainIn);
2176 eiptr = aptr + Source->BufUsed;
2177 len = Source->BufUsed;
2183 eptr = Target->buf + Target->BufSize - 8;
2184 tptr = Target->buf + Target->BufUsed;
2186 while (aptr < eiptr){
2188 IncreaseBuf(Target, 1, -1);
2189 eptr = Target->buf + Target->BufSize - 8;
2190 tptr = Target->buf + Target->BufUsed;
2193 if (*aptr == '\n') {
2197 else if (*aptr == '\r') {
2201 else if (*aptr == '\'') {
2207 Target->BufUsed += 5;
2220 * @ingroup StrBuf_DeEnCoder
2221 * @brief Append a string, escaping characters which have meaning in ICAL.
2223 * @param Target target buffer
2224 * @param Source source buffer; set to NULL if you just have a C-String
2225 * @param PlainIn Plain-C string to append; set to NULL if unused
2227 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2229 const char *aptr, *eiptr;
2233 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2236 if (PlainIn != NULL) {
2238 len = strlen(PlainIn);
2243 eiptr = aptr + Source->BufUsed;
2244 len = Source->BufUsed;
2250 eptr = Target->buf + Target->BufSize - 8;
2251 tptr = Target->buf + Target->BufUsed;
2253 while (aptr < eiptr){
2254 if(tptr + 3 >= eptr) {
2255 IncreaseBuf(Target, 1, -1);
2256 eptr = Target->buf + Target->BufSize - 8;
2257 tptr = Target->buf + Target->BufUsed;
2260 if (*aptr == '\n') {
2267 else if (*aptr == '\r') {
2274 else if (*aptr == ',') {
2290 * @ingroup StrBuf_DeEnCoder
2291 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2293 * @param Target target buffer
2294 * @param Source source buffer; set to NULL if you just have a C-String
2295 * @param PlainIn Plain-C string to append; set to NULL if unused
2296 * @returns size of result or -1
2298 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2300 const char *aptr, *eiptr;
2305 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2308 if (PlainIn != NULL) {
2310 len = strlen(PlainIn);
2315 eiptr = aptr + Source->BufUsed;
2316 len = Source->BufUsed;
2322 bptr = Target->buf + Target->BufUsed;
2323 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2325 while (aptr < eiptr){
2327 IncreaseBuf(Target, 1, -1);
2328 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2329 bptr = Target->buf + Target->BufUsed;
2333 memcpy(bptr, HKEY("\\n"));
2335 Target->BufUsed += 2;
2338 memcpy(bptr, HKEY("\\r"));
2340 Target->BufUsed += 2;
2347 Target->BufUsed += 2;
2350 if ((*(aptr + 1) == 'u') &&
2351 isxdigit(*(aptr + 2)) &&
2352 isxdigit(*(aptr + 3)) &&
2353 isxdigit(*(aptr + 4)) &&
2354 isxdigit(*(aptr + 5)))
2355 { /* oh, a unicode escaper. let it pass through. */
2356 memcpy(bptr, aptr, 6);
2359 Target->BufUsed += 6;
2367 Target->BufUsed += 2;
2375 Target->BufUsed += 2;
2382 Target->BufUsed += 2;
2389 Target->BufUsed += 2;
2392 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2393 while (IsUtf8Sequence > 0){
2396 if (--IsUtf8Sequence)
2404 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2406 return Target->BufUsed;
2410 * @ingroup StrBuf_DeEnCoder
2411 * @brief Append a string, escaping characters which have meaning in HTML + json.
2413 * @param Target target buffer
2414 * @param Source source buffer; set to NULL if you just have a C-String
2415 * @param PlainIn Plain-C string to append; set to NULL if unused
2416 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2417 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2418 * if set to 2, linebreaks are replaced by <br/>
2420 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2422 const char *aptr, *eiptr;
2425 int IsUtf8Sequence = 0;
2427 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2430 if (PlainIn != NULL) {
2432 len = strlen(PlainIn);
2437 eiptr = aptr + Source->BufUsed;
2438 len = Source->BufUsed;
2444 bptr = Target->buf + Target->BufUsed;
2445 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2447 while (aptr < eiptr){
2449 IncreaseBuf(Target, 1, -1);
2450 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2451 bptr = Target->buf + Target->BufUsed;
2455 memcpy(bptr, HKEY("<"));
2457 Target->BufUsed += 4;
2460 memcpy(bptr, HKEY(">"));
2462 Target->BufUsed += 4;
2465 memcpy(bptr, HKEY("&"));
2467 Target->BufUsed += 5;
2480 switch (nolinebreaks) {
2482 *bptr='\0'; /* nothing */
2485 memcpy(bptr, HKEY("<br/>"));
2487 Target->BufUsed += 11;
2490 memcpy(bptr, HKEY("\\n"));
2492 Target->BufUsed += 2;
2496 switch (nolinebreaks) {
2499 *bptr='\0'; /* nothing */
2502 memcpy(bptr, HKEY("\\r"));
2504 Target->BufUsed += 2;
2514 Target->BufUsed += 2;
2517 if ((*(aptr + 1) == 'u') &&
2518 isxdigit(*(aptr + 2)) &&
2519 isxdigit(*(aptr + 3)) &&
2520 isxdigit(*(aptr + 4)) &&
2521 isxdigit(*(aptr + 5)))
2522 { /* oh, a unicode escaper. let it pass through. */
2523 memcpy(bptr, aptr, 6);
2526 Target->BufUsed += 6;
2534 Target->BufUsed += 2;
2542 Target->BufUsed += 2;
2549 Target->BufUsed += 2;
2556 Target->BufUsed += 2;
2560 memcpy(bptr, HKEY(" "));
2562 Target->BufUsed += 6;
2566 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2567 while (IsUtf8Sequence > 0){
2570 if (--IsUtf8Sequence)
2578 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2580 return Target->BufUsed;
2585 * @ingroup StrBuf_DeEnCoder
2586 * @brief replace all non-Ascii characters by another
2587 * @param Buf buffer to inspect
2588 * @param repl charater to stamp over non ascii chars
2590 void StrBufAsciify(StrBuf *Buf, const char repl)
2594 for (offset = 0; offset < Buf->BufUsed; offset ++)
2595 if (!isascii(Buf->buf[offset]))
2596 Buf->buf[offset] = repl;
2601 * @ingroup StrBuf_DeEnCoder
2602 * @brief unhide special chars hidden to the HTML escaper
2603 * @param target buffer to put the unescaped string in
2604 * @param source buffer to unescape
2606 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2611 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2617 FlushStrBuf(target);
2619 len = source->BufUsed;
2620 for (a = 0; a < len; ++a) {
2621 if (target->BufUsed >= target->BufSize)
2622 IncreaseBuf(target, 1, -1);
2624 if (source->buf[a] == '=') {
2625 hex[0] = source->buf[a + 1];
2626 hex[1] = source->buf[a + 2];
2629 sscanf(hex, "%02x", &b);
2630 target->buf[target->BufUsed] = b;
2631 target->buf[++target->BufUsed] = 0;
2635 target->buf[target->BufUsed] = source->buf[a];
2636 target->buf[++target->BufUsed] = 0;
2643 * @ingroup StrBuf_DeEnCoder
2644 * @brief hide special chars from the HTML escapers and friends
2645 * @param target buffer to put the escaped string in
2646 * @param source buffer to escape
2648 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2653 FlushStrBuf(target);
2655 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2660 len = source->BufUsed;
2661 for (i=0; i<len; ++i) {
2662 if (target->BufUsed + 4 >= target->BufSize)
2663 IncreaseBuf(target, 1, -1);
2664 if ( (isalnum(source->buf[i])) ||
2665 (source->buf[i]=='-') ||
2666 (source->buf[i]=='_') ) {
2667 target->buf[target->BufUsed++] = source->buf[i];
2670 sprintf(&target->buf[target->BufUsed],
2672 (0xFF &source->buf[i]));
2673 target->BufUsed += 3;
2676 target->buf[target->BufUsed + 1] = '\0';
2680 /*******************************************************************************
2681 * Quoted Printable de/encoding *
2682 *******************************************************************************/
2685 * @ingroup StrBuf_DeEnCoder
2686 * @brief decode a buffer from base 64 encoding; destroys original
2687 * @param Buf Buffor to transform
2689 int StrBufDecodeBase64(StrBuf *Buf)
2697 xferbuf = (char*) malloc(Buf->BufSize);
2698 if (xferbuf == NULL)
2702 siz = CtdlDecodeBase64(xferbuf,
2712 * @ingroup StrBuf_DeEnCoder
2713 * @brief decode a buffer from base 64 encoding; destroys original
2714 * @param Buf Buffor to transform
2716 int StrBufDecodeHex(StrBuf *Buf)
2719 char *pch, *pche, *pchi;
2721 if (Buf == NULL) return -1;
2723 pch = pchi = Buf->buf;
2724 pche = pch + Buf->BufUsed;
2726 while (pchi < pche){
2727 ch = decode_hex(pchi);
2734 Buf->BufUsed = pch - Buf->buf;
2735 return Buf->BufUsed;
2739 * @ingroup StrBuf_DeEnCoder
2740 * @brief replace all chars >0x20 && < 0x7F with Mute
2741 * @param Mute char to put over invalid chars
2742 * @param Buf Buffor to transform
2744 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2748 if (Buf == NULL) return -1;
2749 pch = (unsigned char *)Buf->buf;
2750 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2751 if ((*pch < 0x20) || (*pch > 0x7F))
2755 return Buf->BufUsed;
2760 * @ingroup StrBuf_DeEnCoder
2761 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2762 * @param Buf Buffer to translate
2763 * @param StripBlanks Reduce several blanks to one?
2765 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2774 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2775 Buf->buf[Buf->BufUsed - 1] = '\0';
2780 while (a < Buf->BufUsed) {
2781 if (Buf->buf[a] == '+')
2783 else if (Buf->buf[a] == '%') {
2784 /* don't let % chars through, rather truncate the input. */
2785 if (a + 2 > Buf->BufUsed) {
2790 hex[0] = Buf->buf[a + 1];
2791 hex[1] = Buf->buf[a + 2];
2794 sscanf(hex, "%02x", &b);
2795 Buf->buf[a] = (char) b;
2796 len = Buf->BufUsed - a - 2;
2798 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2810 * @ingroup StrBuf_DeEnCoder
2811 * @brief RFC2047-encode a header field if necessary.
2812 * If no non-ASCII characters are found, the string
2813 * will be copied verbatim without encoding.
2815 * @param target Target buffer.
2816 * @param source Source string to be encoded.
2817 * @returns encoded length; -1 if non success.
2819 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2821 const char headerStr[] = "=?UTF-8?Q?";
2822 int need_to_encode = 0;
2826 if ((source == NULL) ||
2830 while ((i < source->BufUsed) &&
2831 (!IsEmptyStr (&source->buf[i])) &&
2832 (need_to_encode == 0)) {
2833 if (((unsigned char) source->buf[i] < 32) ||
2834 ((unsigned char) source->buf[i] > 126)) {
2840 if (!need_to_encode) {
2841 if (*target == NULL) {
2842 *target = NewStrBufPlain(source->buf, source->BufUsed);
2845 FlushStrBuf(*target);
2846 StrBufAppendBuf(*target, source, 0);
2849 return (*target)->BufUsed;
2853 if (*target == NULL)
2854 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2855 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2856 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2857 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2858 (*target)->BufUsed = sizeof(headerStr) - 1;
2859 for (i=0; (i < source->BufUsed); ++i) {
2860 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2861 IncreaseBuf(*target, 1, 0);
2862 ch = (unsigned char) source->buf[i];
2872 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2873 (*target)->BufUsed += 3;
2877 (*target)->buf[(*target)->BufUsed] = '_';
2879 (*target)->buf[(*target)->BufUsed] = ch;
2880 (*target)->BufUsed++;
2884 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2885 IncreaseBuf(*target, 1, 0);
2887 (*target)->buf[(*target)->BufUsed++] = '?';
2888 (*target)->buf[(*target)->BufUsed++] = '=';
2889 (*target)->buf[(*target)->BufUsed] = '\0';
2890 return (*target)->BufUsed;;
2895 static void AddRecipient(StrBuf *Target,
2897 StrBuf *EmailAddress,
2902 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2903 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2905 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2906 StrBufRFC2047encode(&EncBuf, UserName);
2907 StrBufAppendBuf(Target, EncBuf, 0);
2908 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2909 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2911 if (StrLength(EmailAddress) > 0){
2912 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2913 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2914 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2920 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2921 * \param Recp Source list of email recipients
2922 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2923 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2924 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2925 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2927 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2929 StrBuf *EmailAddress,
2933 const char *pch, *pche;
2934 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2936 if ((Recp == NULL) || (StrLength(Recp) == 0))
2940 pche = pch + StrLength(Recp);
2942 if (!CheckEncode(pch, -1, pche))
2943 return NewStrBufDup(Recp);
2945 Target = NewStrBufPlain(NULL, StrLength(Recp));
2947 while ((pch != NULL) && (pch < pche))
2949 while (isspace(*pch)) pch++;
2950 UserEnd = EmailStart = EmailEnd = NULL;
2952 if ((*pch == '"') || (*pch == '\'')) {
2953 UserStart = pch + 1;
2955 UserEnd = strchr(UserStart, *pch);
2956 if (UserEnd == NULL)
2957 break; ///TODO: Userfeedback??
2958 EmailStart = UserEnd + 1;
2959 while (isspace(*EmailStart))
2961 if (UserEnd == UserStart) {
2962 UserStart = UserEnd = NULL;
2965 if (*EmailStart == '<') {
2967 EmailEnd = strchr(EmailStart, '>');
2968 if (EmailEnd == NULL)
2969 EmailEnd = strchr(EmailStart, ',');
2973 EmailEnd = strchr(EmailStart, ',');
2975 if (EmailEnd == NULL)
2982 EmailEnd = strchr(UserStart, ',');
2983 if (EmailEnd == NULL) {
2984 EmailEnd = strchr(pch, '>');
2986 if (EmailEnd != NULL) {
2996 while ((EmailEnd > UserStart) && !gt &&
2997 ((*EmailEnd == ',') ||
2998 (*EmailEnd == '>') ||
2999 (isspace(*EmailEnd))))
3001 if (*EmailEnd == '>')
3006 if (EmailEnd == UserStart)
3010 EmailStart = strchr(UserStart, '<');
3011 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3013 UserEnd = EmailStart;
3015 while ((UserEnd > UserStart) &&
3016 isspace (*(UserEnd - 1)))
3019 if (UserStart >= UserEnd)
3020 UserStart = UserEnd = NULL;
3022 else { /* this is a local recipient... no domain, just a realname */
3023 EmailStart = UserStart;
3024 At = strchr(EmailStart, '@');
3030 EmailStart = UserStart;
3036 if ((UserStart != NULL) && (UserEnd != NULL))
3037 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3038 else if ((UserStart != NULL) && (UserEnd == NULL))
3039 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3041 FlushStrBuf(UserName);
3043 if ((EmailStart != NULL) && (EmailEnd != NULL))
3044 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3045 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3046 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3048 FlushStrBuf(EmailAddress);
3050 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3055 if ((pch != NULL) && (*pch == ','))
3057 if (pch != NULL) while (isspace(*pch))
3066 * @brief replaces all occurances of 'search' by 'replace'
3067 * @param buf Buffer to modify
3068 * @param search character to search
3069 * @param replace character to replace search by
3071 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3076 for (i=0; i<buf->BufUsed; i++)
3077 if (buf->buf[i] == search)
3078 buf->buf[i] = replace;
3084 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3085 * @param buf Buffer to modify
3087 void StrBufToUnixLF(StrBuf *buf)
3089 char *pche, *pchS, *pchT;
3093 pche = buf->buf + buf->BufUsed;
3094 pchS = pchT = buf->buf;
3100 if (*pchS != '\n') {
3109 buf->BufUsed = pchT - buf->buf;
3113 /*******************************************************************************
3114 * Iconv Wrapper; RFC822 de/encoding *
3115 *******************************************************************************/
3118 * @ingroup StrBuf_DeEnCoder
3119 * @brief Wrapper around iconv_open()
3120 * Our version adds aliases for non-standard Microsoft charsets
3121 * such as 'MS950', aliasing them to names like 'CP950'
3123 * @param tocode Target encoding
3124 * @param fromcode Source encoding
3125 * @param pic anonimized pointer to iconv struct
3127 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3130 iconv_t ic = (iconv_t)(-1) ;
3131 ic = iconv_open(tocode, fromcode);
3132 if (ic == (iconv_t)(-1) ) {
3133 char alias_fromcode[64];
3134 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3135 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3136 alias_fromcode[0] = 'C';
3137 alias_fromcode[1] = 'P';
3138 ic = iconv_open(tocode, alias_fromcode);
3141 *(iconv_t *)pic = ic;
3147 * @ingroup StrBuf_DeEnCoder
3148 * @brief find one chunk of a RFC822 encoded string
3149 * @param Buffer where to search
3150 * @param bptr where to start searching
3151 * @returns found position, NULL if none.
3153 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3156 /* Find the next ?Q? */
3157 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3160 end = strchr(bptr + 2, '?');
3165 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3166 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3167 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3168 (*(end + 2) == '?')) {
3169 /* skip on to the end of the cluster, the next ?= */
3170 end = strstr(end + 3, "?=");
3173 /* sort of half valid encoding, try to find an end. */
3174 end = strstr(bptr, "?=");
3181 * @ingroup StrBuf_DeEnCoder
3182 * @brief convert one buffer according to the preselected iconv pointer PIC
3183 * @param ConvertBuf buffer we need to translate
3184 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3185 * @param pic Pointer to the iconv-session Object
3187 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3193 char *ibuf; /**< Buffer of characters to be converted */
3194 char *obuf; /**< Buffer for converted characters */
3195 size_t ibuflen; /**< Length of input buffer */
3196 size_t obuflen; /**< Length of output buffer */
3199 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3202 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3203 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3204 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3206 ic = *(iconv_t*)pic;
3207 ibuf = ConvertBuf->buf;
3208 ibuflen = ConvertBuf->BufUsed;
3210 obuflen = TmpBuf->BufSize;
3212 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3215 if (errno == E2BIG) {
3217 IncreaseBuf(TmpBuf, 0, 0);
3222 else if (errno == EILSEQ){
3223 /* hm, invalid utf8 sequence... what to do now? */
3224 /* An invalid multibyte sequence has been encountered in the input */
3226 else if (errno == EINVAL) {
3227 /* An incomplete multibyte sequence has been encountered in the input. */
3230 FlushStrBuf(TmpBuf);
3233 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3234 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3236 /* little card game: wheres the red lady? */
3237 SwapBuffers(ConvertBuf, TmpBuf);
3238 FlushStrBuf(TmpBuf);
3245 * @ingroup StrBuf_DeEnCoder
3246 * @brief catches one RFC822 encoded segment, and decodes it.
3247 * @param Target buffer to fill with result
3248 * @param DecodeMe buffer with stuff to process
3249 * @param SegmentStart points to our current segment in DecodeMe
3250 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3251 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3252 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3253 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3255 inline static void DecodeSegment(StrBuf *Target,
3256 const StrBuf *DecodeMe,
3257 const char *SegmentStart,
3258 const char *SegmentEnd,
3260 StrBuf *ConvertBuf2,
3261 StrBuf *FoundCharset)
3267 iconv_t ic = (iconv_t)(-1);
3271 /* Now we handle foreign character sets properly encoded
3272 * in RFC2047 format.
3274 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3275 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3276 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3277 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3278 if (FoundCharset != NULL) {
3279 FlushStrBuf(FoundCharset);
3280 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3282 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3283 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3285 *encoding = toupper(*encoding);
3286 if (*encoding == 'B') { /**< base64 */
3287 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3288 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3289 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3291 ConvertBuf->BufUsed);
3293 else if (*encoding == 'Q') { /**< quoted-printable */
3297 while (pos < ConvertBuf->BufUsed)
3299 if (ConvertBuf->buf[pos] == '_')
3300 ConvertBuf->buf[pos] = ' ';
3304 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3305 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3307 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3310 ConvertBuf->BufUsed);
3313 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3316 ctdl_iconv_open("UTF-8", charset, &ic);
3317 if (ic != (iconv_t)(-1) ) {
3319 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3320 StrBufAppendBuf(Target, ConvertBuf2, 0);
3325 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3331 * @ingroup StrBuf_DeEnCoder
3332 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3333 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3334 * @param Target where to put the decoded string to
3335 * @param DecodeMe buffer with encoded string
3336 * @param DefaultCharset if we don't find one, which should we use?
3337 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3338 * put it here for later use where no string might be known.
3340 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3343 StrBuf *ConvertBuf2;
3344 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3345 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3347 StrBuf_RFC822_2_Utf8(Target,
3353 FreeStrBuf(&ConvertBuf);
3354 FreeStrBuf(&ConvertBuf2);
3358 * @ingroup StrBuf_DeEnCoder
3359 * @brief Handle subjects with RFC2047 encoding such as:
3360 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3361 * @param Target where to put the decoded string to
3362 * @param DecodeMe buffer with encoded string
3363 * @param DefaultCharset if we don't find one, which should we use?
3364 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3365 * put it here for later use where no string might be known.
3366 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3367 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3369 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3370 const StrBuf *DecodeMe,
3371 const StrBuf* DefaultCharset,
3372 StrBuf *FoundCharset,
3374 StrBuf *ConvertBuf2)
3376 StrBuf *DecodedInvalidBuf = NULL;
3377 const StrBuf *DecodeMee = DecodeMe;
3378 const char *start, *end, *next, *nextend, *ptr = NULL;
3380 iconv_t ic = (iconv_t)(-1) ;
3385 int illegal_non_rfc2047_encoding = 0;
3388 if (DecodeMe == NULL)
3390 /* Sometimes, badly formed messages contain strings which were simply
3391 * written out directly in some foreign character set instead of
3392 * using RFC2047 encoding. This is illegal but we will attempt to
3393 * handle it anyway by converting from a user-specified default
3394 * charset to UTF-8 if we see any nonprintable characters.
3397 for (i=0; i<DecodeMe->BufUsed; ++i) {
3398 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3399 illegal_non_rfc2047_encoding = 1;
3404 if ((illegal_non_rfc2047_encoding) &&
3405 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3406 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3409 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3410 if (ic != (iconv_t)(-1) ) {
3411 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3412 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3413 DecodeMee = DecodedInvalidBuf;
3419 /* pre evaluate the first pair */
3421 start = strstr(DecodeMee->buf, "=?");
3422 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3424 end = FindNextEnd (DecodeMee, start + 2);
3426 StrBufAppendBuf(Target, DecodeMee, 0);
3427 FreeStrBuf(&DecodedInvalidBuf);
3432 if (start != DecodeMee->buf) {
3435 nFront = start - DecodeMee->buf;
3436 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3439 * Since spammers will go to all sorts of absurd lengths to get their
3440 * messages through, there are LOTS of corrupt headers out there.
3441 * So, prevent a really badly formed RFC2047 header from throwing
3442 * this function into an infinite loop.
3444 while ((start != NULL) &&
3451 DecodeSegment(Target,
3459 next = strstr(end, "=?");
3461 if ((next != NULL) &&
3463 nextend = FindNextEnd(DecodeMee, next);
3464 if (nextend == NULL)
3467 /* did we find two partitions */
3468 if ((next != NULL) &&
3472 while ((ptr < next) &&
3479 * did we find a gab just filled with blanks?
3480 * if not, copy its stuff over.
3484 StrBufAppendBufPlain(Target,
3490 /* our next-pair is our new first pair now. */
3496 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3497 if ((end != NULL) && (end < nextend)) {
3499 while ( (ptr < nextend) &&
3506 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3508 FreeStrBuf(&DecodedInvalidBuf);
3511 /*******************************************************************************
3512 * Manipulating UTF-8 Strings *
3513 *******************************************************************************/
3517 * @brief evaluate the length of an utf8 special character sequence
3518 * @param Char the character to examine
3519 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3521 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3524 unsigned char test = (1<<7);
3526 if ((*CharS & 0xC0) != 0xC0)
3530 ((test & ((unsigned char)*CharS)) != 0))
3535 if ((n > 6) || ((CharE - CharS) < n))
3542 * @brief detect whether this char starts an utf-8 encoded char
3543 * @param Char character to inspect
3544 * @returns yes or no
3546 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3548 /** 11??.???? indicates an UTF8 Sequence. */
3549 return ((Char & 0xC0) == 0xC0);
3554 * @brief measure the number of glyphs in an UTF8 string...
3555 * @param Buf string to measure
3556 * @returns the number of glyphs in Buf
3558 long StrBuf_Utf8StrLen(StrBuf *Buf)
3564 if ((Buf == NULL) || (Buf->BufUsed == 0))
3567 eptr = Buf->buf + Buf->BufUsed;
3568 while ((aptr < eptr) && (*aptr != '\0')) {
3569 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3570 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3571 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3584 * @brief cuts a string after maxlen glyphs
3585 * @param Buf string to cut to maxlen glyphs
3586 * @param maxlen how long may the string become?
3587 * @returns current length of the string
3589 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3595 eptr = Buf->buf + Buf->BufUsed;
3596 while ((aptr < eptr) && (*aptr != '\0')) {
3597 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3598 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3599 while ((*aptr++ != '\0') && (m-- > 0));
3608 Buf->BufUsed = aptr - Buf->buf;
3609 return Buf->BufUsed;
3612 return Buf->BufUsed;
3620 /*******************************************************************************
3622 *******************************************************************************/
3625 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3626 #define OS_CODE 0x03 /*< unix */
3629 * @ingroup StrBuf_DeEnCoder
3630 * @brief uses the same calling syntax as compress2(), but it
3631 * creates a stream compatible with HTTP "Content-encoding: gzip"
3632 * @param dest compressed buffer
3633 * @param destLen length of the compresed data
3634 * @param source source to encode
3635 * @param sourceLen length of source to encode
3636 * @param level compression level
3638 int ZEXPORT compress_gzip(Bytef * dest,
3640 const Bytef * source,
3644 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3646 /* write gzip header */
3647 snprintf((char *) dest, *destLen,
3648 "%c%c%c%c%c%c%c%c%c%c",
3649 gz_magic[0], gz_magic[1], Z_DEFLATED,
3650 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3653 /* normal deflate */
3656 stream.next_in = (Bytef *) source;
3657 stream.avail_in = (uInt) sourceLen;
3658 stream.next_out = dest + 10L; // after header
3659 stream.avail_out = (uInt) * destLen;
3660 if ((uLong) stream.avail_out != *destLen)
3663 stream.zalloc = (alloc_func) 0;
3664 stream.zfree = (free_func) 0;
3665 stream.opaque = (voidpf) 0;
3667 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3668 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3672 err = deflate(&stream, Z_FINISH);
3673 if (err != Z_STREAM_END) {
3674 deflateEnd(&stream);
3675 return err == Z_OK ? Z_BUF_ERROR : err;
3677 *destLen = stream.total_out + 10L;
3679 /* write CRC and Length */
3680 uLong crc = crc32(0L, source, sourceLen);
3682 for (n = 0; n < 4; ++n, ++*destLen) {
3683 dest[*destLen] = (int) (crc & 0xff);
3686 uLong len = stream.total_in;
3687 for (n = 0; n < 4; ++n, ++*destLen) {
3688 dest[*destLen] = (int) (len & 0xff);
3691 err = deflateEnd(&stream);
3698 * @ingroup StrBuf_DeEnCoder
3699 * @brief compress the buffer with gzip
3700 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3701 * @param Buf buffer whose content is to be gzipped
3703 int CompressBuffer(StrBuf *Buf)
3706 char *compressed_data = NULL;
3707 size_t compressed_len, bufsize;
3710 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3711 compressed_data = malloc(compressed_len);
3713 if (compressed_data == NULL)
3715 /* Flush some space after the used payload so valgrind shuts up... */
3716 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3717 Buf->buf[Buf->BufUsed + i++] = '\0';
3718 if (compress_gzip((Bytef *) compressed_data,
3721 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3724 Buf->buf = compressed_data;
3725 Buf->BufUsed = compressed_len;
3726 Buf->BufSize = bufsize;
3727 /* Flush some space after the used payload so valgrind shuts up... */
3729 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3730 Buf->buf[Buf->BufUsed + i++] = '\0';
3733 free(compressed_data);
3735 #endif /* HAVE_ZLIB */
3739 /*******************************************************************************
3740 * File I/O; Callbacks to libevent *
3741 *******************************************************************************/
3743 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3748 if ((FB == NULL) || (FB->Buf == NULL))
3752 * check whether the read pointer is somewhere in a range
3753 * where a cut left is inexpensive
3756 if (FB->ReadWritePointer != NULL)
3760 already_read = FB->ReadWritePointer - FB->Buf->buf;
3761 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3763 if (already_read != 0) {
3766 unread = FB->Buf->BufUsed - already_read;
3768 /* else nothing to compact... */
3770 FB->ReadWritePointer = FB->Buf->buf;
3771 bufremain = FB->Buf->BufSize;
3773 else if ((unread < 64) ||
3774 (bufremain < already_read))
3777 * if its just a tiny bit remaining, or we run out of space...
3780 FB->Buf->BufUsed = unread;
3781 if (unread < already_read)
3782 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3784 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3785 FB->ReadWritePointer = FB->Buf->buf;
3786 bufremain = FB->Buf->BufSize - unread - 1;
3788 else if (bufremain < (FB->Buf->BufSize / 10))
3790 /* get a bigger buffer */
3792 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3794 FB->ReadWritePointer = FB->Buf->buf + unread;
3796 bufremain = FB->Buf->BufSize - unread - 1;
3797 /*TODO: special increase function that won't copy the already read! */
3800 else if (bufremain < 10) {
3801 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3803 FB->ReadWritePointer = FB->Buf->buf;
3805 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3810 FB->ReadWritePointer = FB->Buf->buf;
3811 bufremain = FB->Buf->BufSize - 1;
3814 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3817 FB->Buf->BufUsed += n;
3818 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3823 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3828 if ((FB == NULL) || (FB->Buf == NULL))
3831 if (FB->ReadWritePointer != NULL)
3833 WriteRemain = FB->Buf->BufUsed -
3834 (FB->ReadWritePointer -
3838 FB->ReadWritePointer = FB->Buf->buf;
3839 WriteRemain = FB->Buf->BufUsed;
3842 n = write(fd, FB->ReadWritePointer, WriteRemain);
3844 FB->ReadWritePointer += n;
3846 if (FB->ReadWritePointer ==
3847 FB->Buf->buf + FB->Buf->BufUsed)
3849 FlushStrBuf(FB->Buf);
3850 FB->ReadWritePointer = NULL;
3853 // check whether we've got something to write
3854 // get the maximum chunk plus the pointer we can send
3855 // write whats there
3856 // if not all was sent, remember the send pointer for the next time
3857 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3863 * @ingroup StrBuf_IO
3864 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3865 * @param LineBuf your line will be copied here.
3866 * @param FB BLOB with lines of text...
3867 * @param Ptr moved arround to keep the next-line across several iterations
3868 * has to be &NULL on start; will be &NotNULL on end of buffer
3869 * @returns size of copied buffer
3871 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3873 const char *aptr, *ptr, *eptr;
3876 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3880 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3881 FB->ReadWritePointer = StrBufNOTNULL;
3885 FlushStrBuf(LineBuf);
3886 if (FB->ReadWritePointer == NULL)
3887 ptr = aptr = FB->Buf->buf;
3889 ptr = aptr = FB->ReadWritePointer;
3891 optr = LineBuf->buf;
3892 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3893 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3895 while ((ptr <= eptr) &&
3902 LineBuf->BufUsed = optr - LineBuf->buf;
3903 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3904 optr = LineBuf->buf + LineBuf->BufUsed;
3905 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3910 if (optr > LineBuf->buf)
3912 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3913 LineBuf->BufUsed = optr - LineBuf->buf;
3915 if ((FB->ReadWritePointer != NULL) &&
3916 (FB->ReadWritePointer != FB->Buf->buf))
3918 /* Ok, the client application read all the data
3919 it was interested in so far. Since there is more to read,
3920 we now shrink the buffer, and move the rest over.
3922 StrBufCutLeft(FB->Buf,
3923 FB->ReadWritePointer - FB->Buf->buf);
3924 FB->ReadWritePointer = FB->Buf->buf;
3926 return eMustReadMore;
3929 LineBuf->BufUsed = optr - LineBuf->buf;
3931 if ((ptr <= eptr) && (*ptr == '\r'))
3933 if ((ptr <= eptr) && (*ptr == '\n'))
3937 FB->ReadWritePointer = ptr;
3940 FlushStrBuf(FB->Buf);
3941 FB->ReadWritePointer = NULL;
3944 return eReadSuccess;
3948 * @ingroup StrBuf_CHUNKED_IO
3949 * @brief check whether the chunk-buffer has more data waiting or not.
3950 * @param FB Chunk-Buffer to inspect
3952 eReadState StrBufCheckBuffer(IOBuffer *FB)
3956 if (FB->Buf->BufUsed == 0)
3957 return eReadSuccess;
3958 if (FB->ReadWritePointer == NULL)
3959 return eBufferNotEmpty;
3960 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3961 return eBufferNotEmpty;
3962 return eReadSuccess;
3965 long IOBufferStrLength(IOBuffer *FB)
3967 if ((FB == NULL) || (FB->Buf == NULL))
3969 if (FB->ReadWritePointer == NULL)
3970 return StrLength(FB->Buf);
3972 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3975 inline static void FDIOBufferFlush(FDIOBuffer *FDB)
3977 memset(FDB, 0, sizeof(FDIOBuffer));
3979 FDB->SplicePipe[0] = -1;
3980 FDB->SplicePipe[1] = -1;
3983 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3985 FDIOBufferFlush(FDB);
3987 FDB->TotalSendSize = TotalSendSize;
3991 pipe(FDB->SplicePipe);
3994 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3999 void FDIOBufferDelete(FDIOBuffer *FDB)
4004 if (FDB->SplicePipe[0] > 0)
4005 close(FDB->SplicePipe[0]);
4006 if (FDB->SplicePipe[1] > 0)
4007 close(FDB->SplicePipe[1]);
4011 FreeStrBuf(&FDB->ChunkBuffer);
4013 if (FDB->OtherFD > 0)
4014 close(FDB->OtherFD);
4015 FDIOBufferFlush(FDB);
4018 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
4020 ssize_t sent, pipesize;
4024 if (FDB->PipeSize == 0)
4026 pipesize = splice(FDB->OtherFD,
4027 &FDB->TotalSentAlready,
4030 FDB->ChunkSendRemain,
4035 *Err = strerror(errno);
4038 FDB->PipeSize = pipesize;
4040 sent = splice(FDB->SplicePipe[0],
4045 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
4048 *Err = strerror(errno);
4051 FDB->PipeSize -= sent;
4052 FDB->ChunkSendRemain -= sent;
4061 pRead = FDB->ChunkBuffer->buf;
4062 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
4064 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
4066 FDB->ChunkBuffer->BufUsed += nRead;
4067 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
4069 else if (nRead == 0) {}
4074 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
4077 FDB->TotalSentAlready += nRead;
4078 FDB->ChunkSendRemain -= nRead;
4079 return FDB->ChunkSendRemain;
4087 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
4089 ssize_t sent, pipesize;
4094 if (FDB->PipeSize == 0)
4096 pipesize = splice(FDB->IOB->fd,
4100 FDB->ChunkSendRemain,
4101 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4105 *Err = strerror(errno);
4108 FDB->PipeSize = pipesize;
4111 sent = splice(FDB->SplicePipe[0],
4114 &FDB->TotalSentAlready,
4116 SPLICE_F_MORE | SPLICE_F_MOVE);
4120 *Err = strerror(errno);
4123 FDB->PipeSize -= sent;
4124 FDB->ChunkSendRemain -= sent;
4130 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4135 FDB->ChunkBuffer->BufUsed = sent;
4137 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4138 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4140 *Err = strerror(errno);
4146 FDB->ChunkBuffer->BufUsed = 0;
4147 FDB->TotalSentAlready += sent;
4148 FDB->ChunkSendRemain -= sent;
4149 return FDB->ChunkSendRemain;
4151 else if (sent < 0) {
4152 *Err = strerror(errno);
4159 int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
4161 ssize_t sent, pipesize;
4166 if (FDB->PipeSize == 0)
4168 pipesize = splice(FDB->IOB->fd,
4169 &FDB->TotalReadAlready,
4172 FDB->ChunkSendRemain,
4173 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
4177 *Err = strerror(errno);
4180 FDB->PipeSize = pipesize;
4183 sent = splice(FDB->SplicePipe[0],
4186 &FDB->TotalSentAlready,
4188 SPLICE_F_MORE | SPLICE_F_MOVE);
4192 *Err = strerror(errno);
4195 FDB->PipeSize -= sent;
4196 FDB->ChunkSendRemain -= sent;
4202 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4207 FDB->ChunkBuffer->BufUsed = sent;
4209 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4210 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4212 *Err = strerror(errno);
4218 FDB->ChunkBuffer->BufUsed = 0;
4219 FDB->TotalSentAlready += sent;
4220 FDB->ChunkSendRemain -= sent;
4221 return FDB->ChunkSendRemain;
4223 else if (sent < 0) {
4224 *Err = strerror(errno);
4231 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4237 int nSuccessLess = 0;
4241 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4242 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4244 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4245 (FDB->ChunkSendRemain > 0))
4248 tv.tv_sec = 1; /* selectresolution; */
4252 FD_SET(FDB->OtherFD, &rfds);
4253 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4254 *Error = strerror(errno);
4258 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4263 should_write = FDB->IOB->Buf->BufUsed -
4264 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4265 if (should_write > FDB->ChunkSendRemain)
4266 should_write = FDB->ChunkSendRemain;
4268 rlen = write(FDB->OtherFD,
4269 FDB->IOB->ReadWritePointer,
4272 *Error = strerror(errno);
4276 FDB->TotalSentAlready += rlen;
4277 FDB->IOB->ReadWritePointer += rlen;
4278 FDB->ChunkSendRemain -= rlen;
4280 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4282 FlushStrBuf(FDB->IOB->Buf);
4283 FDB->IOB->ReadWritePointer = NULL;
4286 if (FDB->ChunkSendRemain == 0)
4287 return eReadSuccess;
4289 return eMustReadMore;
4292 /*******************************************************************************
4293 * File I/O; Prefer buffered read since its faster! *
4294 *******************************************************************************/
4297 * @ingroup StrBuf_IO
4298 * @brief Read a line from socket
4299 * flushes and closes the FD on error
4300 * @param buf the buffer to get the input to
4301 * @param fd pointer to the filedescriptor to read
4302 * @param append Append to an existing string or replace?
4303 * @param Error strerror() on error
4304 * @returns numbers of chars read
4306 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4308 int len, rlen, slen;
4310 if ((buf == NULL) || (buf->buf == NULL)) {
4311 *Error = strerror(EINVAL);
4318 slen = len = buf->BufUsed;
4320 rlen = read(*fd, &buf->buf[len], 1);
4322 *Error = strerror(errno);
4329 if (buf->buf[len] == '\n')
4331 if (buf->buf[len] != '\r')
4333 if (len + 2 >= buf->BufSize) {
4335 buf->buf[len+1] = '\0';
4336 IncreaseBuf(buf, 1, -1);
4340 buf->buf[len] = '\0';
4345 * @ingroup StrBuf_BufferedIO
4346 * @brief Read a line from socket
4347 * flushes and closes the FD on error
4348 * @param Line the line to read from the fd / I/O Buffer
4349 * @param buf the buffer to get the input to
4350 * @param fd pointer to the filedescriptor to read
4351 * @param timeout number of successless selects until we bail out
4352 * @param selectresolution how long to wait on each select
4353 * @param Error strerror() on error
4354 * @returns numbers of chars read
4356 int StrBufTCP_read_buffered_line(StrBuf *Line,
4360 int selectresolution,
4364 int nSuccessLess = 0;
4371 if (buf->BufUsed > 0) {
4372 pch = strchr(buf->buf, '\n');
4375 len = pch - buf->buf;
4376 if (len > 0 && (*(pch - 1) == '\r') )
4378 StrBufSub(Line, buf, 0, len - rlen);
4379 StrBufCutLeft(buf, len + 1);
4384 if (buf->BufSize - buf->BufUsed < 10)
4385 IncreaseBuf(buf, 1, -1);
4387 fdflags = fcntl(*fd, F_GETFL);
4388 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4390 while ((nSuccessLess < timeout) && (pch == NULL)) {
4392 tv.tv_sec = selectresolution;
4397 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4398 *Error = strerror(errno);
4404 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4409 &buf->buf[buf->BufUsed],
4410 buf->BufSize - buf->BufUsed - 1);
4412 *Error = strerror(errno);
4417 else if (rlen > 0) {
4419 buf->BufUsed += rlen;
4420 buf->buf[buf->BufUsed] = '\0';
4421 pch = strchr(buf->buf, '\n');
4422 if ((pch == NULL) &&
4423 (buf->BufUsed + 10 > buf->BufSize) &&
4424 (IncreaseBuf(buf, 1, -1) == -1))
4432 len = pch - buf->buf;
4433 if (len > 0 && (*(pch - 1) == '\r') )
4435 StrBufSub(Line, buf, 0, len - rlen);
4436 StrBufCutLeft(buf, len + 1);
4443 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4444 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4445 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4447 * @ingroup StrBuf_BufferedIO
4448 * @brief Read a line from socket
4449 * flushes and closes the FD on error
4450 * @param Line where to append our Line read from the fd / I/O Buffer;
4451 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4452 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4453 * @param fd pointer to the filedescriptor to read
4454 * @param timeout number of successless selects until we bail out
4455 * @param selectresolution how long to wait on each select
4456 * @param Error strerror() on error
4457 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4459 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4464 int selectresolution,
4467 const char *pche = NULL;
4468 const char *pos = NULL;
4470 int len, rlen, retlen;
4471 int nSuccessLess = 0;
4473 const char *pch = NULL;
4479 if ((Line == NULL) ||
4486 *Error = ErrRBLF_PreConditionFailed;
4491 if ((IOBuf->BufUsed > 0) &&
4493 (pos < IOBuf->buf + IOBuf->BufUsed))
4497 pche = IOBuf->buf + IOBuf->BufUsed;
4501 while ((pch < pche) && (*pch != '\n'))
4503 if (Line->BufUsed + 10 > Line->BufSize)
4506 apos = pcht - Line->buf;
4508 IncreaseBuf(Line, 1, -1);
4509 pcht = Line->buf + apos;
4517 if (len > 0 && (*(pch - 1) == '\r') )
4526 if ((pch >= pche) || (*pch == '\0'))
4534 if ((pch != NULL) &&
4537 if (pch + 1 >= pche) {
4550 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4552 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4553 IncreaseBuf(IOBuf, 1, -1);
4555 fdflags = fcntl(*fd, F_GETFL);
4556 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4559 while ((nSuccessLess < timeout) &&
4569 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4570 *Error = strerror(errno);
4574 *Error = ErrRBLF_SelectFailed;
4577 if (! FD_ISSET(*fd, &rfds) != 0) {
4583 &IOBuf->buf[IOBuf->BufUsed],
4584 IOBuf->BufSize - IOBuf->BufUsed - 1);
4586 *Error = strerror(errno);
4591 else if (rlen > 0) {
4593 pLF = IOBuf->buf + IOBuf->BufUsed;
4594 IOBuf->BufUsed += rlen;
4595 IOBuf->buf[IOBuf->BufUsed] = '\0';
4597 pche = IOBuf->buf + IOBuf->BufUsed;
4599 while ((pLF < pche) && (*pLF != '\n'))
4601 if ((pLF >= pche) || (*pLF == '\0'))
4604 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4608 if (pLF != NULL) apos = pLF - IOBuf->buf;
4609 IncreaseBuf(IOBuf, 1, -1);
4610 if (pLF != NULL) pLF = IOBuf->buf + apos;
4624 if (len > 0 && (*(pLF - 1) == '\r') )
4626 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4627 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4633 return retlen + len;
4635 *Error = ErrRBLF_NotEnoughSentFromServer;
4640 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4642 * @ingroup StrBuf_IO
4643 * @brief Input binary data from socket
4644 * flushes and closes the FD on error
4645 * @param Buf the buffer to get the input to
4646 * @param fd pointer to the filedescriptor to read
4647 * @param append Append to an existing string or replace?
4648 * @param nBytes the maximal number of bytes to read
4649 * @param Error strerror() on error
4650 * @returns numbers of chars read
4652 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4663 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4665 *Error = ErrRBLF_BLOBPreConditionFailed;
4670 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4671 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4673 ptr = Buf->buf + Buf->BufUsed;
4675 fdflags = fcntl(*fd, F_GETFL);
4676 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4678 while ((nRead < nBytes) &&
4688 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4689 *Error = strerror(errno);
4693 *Error = ErrRBLF_SelectFailed;
4696 if (! FD_ISSET(*fd, &rfds) != 0) {
4702 if ((rlen = read(*fd,
4704 nBytes - nRead)) == -1) {
4707 *Error = strerror(errno);
4712 Buf->BufUsed += rlen;
4714 Buf->buf[Buf->BufUsed] = '\0';
4718 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4719 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4721 * @ingroup StrBuf_BufferedIO
4722 * @brief Input binary data from socket
4723 * flushes and closes the FD on error
4724 * @param Blob put binary thing here
4725 * @param IOBuf the buffer to get the input to
4726 * @param Pos offset inside of IOBuf
4727 * @param fd pointer to the filedescriptor to read
4728 * @param append Append to an existing string or replace?
4729 * @param nBytes the maximal number of bytes to read
4730 * @param check whether we should search for '000\n' terminators in case of timeouts
4731 * @param Error strerror() on error
4732 * @returns numbers of chars read
4734 int StrBufReadBLOBBuffered(StrBuf *Blob,
4747 int nAlreadyRead = 0;
4752 int nSuccessLess = 0;
4755 if ((Blob == NULL) ||
4762 *Error = ErrRBB_BLOBFPreConditionFailed;
4768 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4769 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4774 rlen = pos - IOBuf->buf;
4775 rlen = IOBuf->BufUsed - rlen;
4778 if ((IOBuf->BufUsed > 0) &&
4780 (pos < IOBuf->buf + IOBuf->BufUsed))
4782 if (rlen < nBytes) {
4783 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4784 Blob->BufUsed += rlen;
4785 Blob->buf[Blob->BufUsed] = '\0';
4786 nAlreadyRead = nRead = rlen;
4789 if (rlen >= nBytes) {
4790 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4791 Blob->BufUsed += nBytes;
4792 Blob->buf[Blob->BufUsed] = '\0';
4793 if (rlen == nBytes) {
4805 if (IOBuf->BufSize < nBytes - nRead)
4806 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4809 fdflags = fcntl(*fd, F_GETFL);
4810 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4818 while ((nSuccessLess < MaxTries) &&
4828 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4829 *Error = strerror(errno);
4833 *Error = ErrRBLF_SelectFailed;
4836 if (! FD_ISSET(*fd, &rfds) != 0) {
4843 IOBuf->BufSize - (ptr - IOBuf->buf));
4847 *Error = strerror(errno);
4850 else if (rlen == 0){
4851 if ((check == NNN_TERM) &&
4853 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4855 StrBufPlain(Blob, HKEY("\n000\n"));
4856 StrBufCutRight(Blob, 5);
4857 return Blob->BufUsed;
4859 else if (!IsNonBlock)
4861 else if (nSuccessLess > MaxTries) {
4863 *Error = ErrRBB_too_many_selects;
4867 else if (rlen > 0) {
4871 IOBuf->BufUsed += rlen;
4874 if (nSuccessLess >= MaxTries) {
4876 *Error = ErrRBB_too_many_selects;
4880 if (nRead > nBytes) {
4881 *Pos = IOBuf->buf + nBytes;
4883 Blob->buf[Blob->BufUsed] = '\0';
4884 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4888 return nRead + nAlreadyRead;
4892 * @ingroup StrBuf_IO
4893 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4894 * @param LineBuf your line will be copied here.
4895 * @param Buf BLOB with lines of text...
4896 * @param Ptr moved arround to keep the next-line across several iterations
4897 * has to be &NULL on start; will be &NotNULL on end of buffer
4898 * @returns size of remaining buffer
4900 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4902 const char *aptr, *ptr, *eptr;
4905 if ((Buf == NULL) ||
4906 (*Ptr == StrBufNOTNULL) ||
4908 (LineBuf->buf == NULL))
4910 *Ptr = StrBufNOTNULL;
4914 FlushStrBuf(LineBuf);
4916 ptr = aptr = Buf->buf;
4920 optr = LineBuf->buf;
4921 eptr = Buf->buf + Buf->BufUsed;
4922 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4924 while ((ptr <= eptr) &&
4931 LineBuf->BufUsed = optr - LineBuf->buf;
4932 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4933 optr = LineBuf->buf + LineBuf->BufUsed;
4934 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4938 if ((ptr >= eptr) && (optr > LineBuf->buf))
4940 LineBuf->BufUsed = optr - LineBuf->buf;
4942 if ((ptr <= eptr) && (*ptr == '\r'))
4944 if ((ptr <= eptr) && (*ptr == '\n'))
4951 *Ptr = StrBufNOTNULL;
4954 return Buf->BufUsed - (ptr - Buf->buf);
4959 * @ingroup StrBuf_IO
4960 * @brief removes double slashes from pathnames
4961 * @param Dir directory string to filter
4962 * @param RemoveTrailingSlash allows / disallows trailing slashes
4964 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4970 while (!IsEmptyStr(a)) {
4982 if ((RemoveTrailingSlash) &&
4988 Dir->BufUsed = b - Dir->buf;