2 * Copyright (c) 1987-2011 by the citadel.org team
4 * This program is open source software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 3 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <sys/select.h>
29 #include <sys/types.h>
30 #define SHOW_ME_VAPPEND_PRINTF
33 #include "libcitadel.h"
44 #include <sys/sendfile.h>
49 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
50 const Bytef * source, uLong sourceLen, int level);
52 int BaseStrBufSize = 64;
54 const char *StrBufNOTNULL = ((char*) NULL) - 1;
56 const char HexList[256][3] = {
57 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
58 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
59 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
60 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
61 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
62 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
63 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
64 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
65 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
66 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
67 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
68 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
69 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
70 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
71 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
72 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
75 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
76 * StrBuf is a versatile class, aiding the handling of dynamic strings
77 * * reduce de/reallocations
78 * * reduce the need to remeasure it
79 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
80 * * allow asyncroneous IO for line and Blob based operations
81 * * reduce the use of memove in those
82 * * Quick filling in several operations with append functions
86 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
91 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
93 * use these operators to interfere with code demanding char*;
94 * if you need to own the content, smash me. Avoid, since we loose the length information.
98 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
100 * operations to get your Strings into a StrBuf, manipulating them, or appending
103 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
105 * Quick tokenizer; demands of the user to pull its tokens in sequence
109 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
111 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
115 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
117 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
118 * External Cursor to maintain the current read position inside of the buffer
119 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
123 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
129 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
131 * these functions translate the content of a buffer into another representation;
132 * some are combined Fillers and encoders
136 * Private Structure for the Stringbuffer
139 char *buf; /**< the pointer to the dynamic buffer */
140 long BufSize; /**< how many spcae do we optain */
141 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
142 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
144 long nIncreases; /**< for profiling; cound how many times we needed more */
145 char bt [SIZ]; /**< Stacktrace of last increase */
146 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
151 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
152 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
155 #ifdef HAVE_BACKTRACE
156 static void StrBufBacktrace(StrBuf *Buf, int which)
160 void *stack_frames[50];
165 pstart = pch = Buf->bt;
167 pstart = pch = Buf->bt_lastinc;
168 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
169 strings = backtrace_symbols(stack_frames, size);
170 for (i = 0; i < size; i++) {
172 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
174 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
183 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
185 if (hFreeDbglog == -1){
186 pid_t pid = getpid();
188 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
189 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
191 if ((*FreeMe)->nIncreases > 0)
195 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
197 (*FreeMe)->nIncreases,
201 (*FreeMe)->bt_lastinc);
202 n = write(hFreeDbglog, buf, n);
208 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
212 n = write(hFreeDbglog, buf, n);
216 void dbg_IncreaseBuf(StrBuf *IncMe)
219 #ifdef HAVE_BACKTRACE
220 StrBufBacktrace(Buf, 1);
224 void dbg_Init(StrBuf *Buf)
228 Buf->bt_lastinc[0] = '\0';
229 #ifdef HAVE_BACKTRACE
230 StrBufBacktrace(Buf, 0);
236 #define dbg_FreeStrBuf(a, b)
237 #define dbg_IncreaseBuf(a)
244 * @brief swaps the contents of two StrBufs
245 * this is to be used to have cheap switched between a work-buffer and a target buffer
247 * @param B second one
249 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
253 memcpy(&C, A, sizeof(*A));
254 memcpy(A, B, sizeof(*B));
255 memcpy(B, &C, sizeof(C));
260 * @ingroup StrBuf_Cast
261 * @brief Cast operator to Plain String
262 * @note if the buffer is altered by StrBuf operations, this pointer may become
263 * invalid. So don't lean on it after altering the buffer!
264 * Since this operation is considered cheap, rather call it often than risking
265 * your pointer to become invalid!
266 * @param Str the string we want to get the c-string representation for
267 * @returns the Pointer to the Content. Don't mess with it!
269 inline const char *ChrPtr(const StrBuf *Str)
277 * @ingroup StrBuf_Cast
278 * @brief since we know strlen()'s result, provide it here.
279 * @param Str the string to return the length to
280 * @returns contentlength of the buffer
282 inline int StrLength(const StrBuf *Str)
284 return (Str != NULL) ? Str->BufUsed : 0;
288 * @ingroup StrBuf_DeConstructors
289 * @brief local utility function to resize the buffer
290 * @param Buf the buffer whichs storage we should increase
291 * @param KeepOriginal should we copy the original buffer or just start over with a new one
292 * @param DestSize what should fit in after?
294 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
297 size_t NewSize = Buf->BufSize * 2;
303 while ((NewSize <= DestSize) && (NewSize != 0))
309 NewBuf= (char*) malloc(NewSize);
313 if (KeepOriginal && (Buf->BufUsed > 0))
315 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
324 Buf->BufSize = NewSize;
326 dbg_IncreaseBuf(Buf);
332 * @ingroup StrBuf_DeConstructors
333 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
334 * @param Buf Buffer to shrink (has to be empty)
335 * @param ThreshHold if the buffer is bigger then this, its readjusted
336 * @param NewSize if we Shrink it, how big are we going to be afterwards?
338 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
341 (Buf->BufUsed == 0) &&
342 (Buf->BufSize < ThreshHold)) {
344 Buf->buf = (char*) malloc(NewSize);
346 Buf->BufSize = NewSize;
351 * @ingroup StrBuf_DeConstructors
352 * @brief shrink long term buffers to their real size so they don't waste memory
353 * @param Buf buffer to shrink
354 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
355 * @returns physical size of the buffer
357 long StrBufShrinkToFit(StrBuf *Buf, int Force)
362 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
364 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
365 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
366 Buf->BufSize = Buf->BufUsed + 1;
374 * @ingroup StrBuf_DeConstructors
375 * @brief Allocate a new buffer with default buffer size
376 * @returns the new stringbuffer
378 StrBuf* NewStrBuf(void)
382 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
383 NewBuf->buf = (char*) malloc(BaseStrBufSize);
384 NewBuf->buf[0] = '\0';
385 NewBuf->BufSize = BaseStrBufSize;
387 NewBuf->ConstBuf = 0;
395 * @ingroup StrBuf_DeConstructors
396 * @brief Copy Constructor; returns a duplicate of CopyMe
397 * @param CopyMe Buffer to faxmilate
398 * @returns the new stringbuffer
400 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
407 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
408 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
409 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
410 NewBuf->BufUsed = CopyMe->BufUsed;
411 NewBuf->BufSize = CopyMe->BufSize;
412 NewBuf->ConstBuf = 0;
420 * @ingroup StrBuf_DeConstructors
421 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
422 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
423 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
424 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
425 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
426 * @returns the new stringbuffer
428 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
432 if (CreateRelpaceMe == NULL)
437 if (*CreateRelpaceMe != NULL)
438 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
440 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
444 if (CopyFlushMe == NULL)
446 if (*CreateRelpaceMe != NULL)
447 FlushStrBuf(*CreateRelpaceMe);
449 *CreateRelpaceMe = NewStrBuf();
454 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
455 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
456 * be a big IO-Buffer...
458 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
460 if (*CreateRelpaceMe == NULL)
462 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
467 NewBuf = *CreateRelpaceMe;
470 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
474 if (*CreateRelpaceMe == NULL)
476 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
480 NewBuf = *CreateRelpaceMe;
481 SwapBuffers (NewBuf, CopyFlushMe);
484 FlushStrBuf(CopyFlushMe);
489 * @ingroup StrBuf_DeConstructors
490 * @brief create a new Buffer using an existing c-string
491 * this function should also be used if you want to pre-suggest
492 * the buffer size to allocate in conjunction with ptr == NULL
493 * @param ptr the c-string to copy; may be NULL to create a blank instance
494 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
495 * @returns the new stringbuffer
497 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
500 size_t Siz = BaseStrBufSize;
503 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
505 CopySize = strlen((ptr != NULL)?ptr:"");
509 while ((Siz <= CopySize) && (Siz != 0))
518 NewBuf->buf = (char*) malloc(Siz);
519 if (NewBuf->buf == NULL)
524 NewBuf->BufSize = Siz;
526 memcpy(NewBuf->buf, ptr, CopySize);
527 NewBuf->buf[CopySize] = '\0';
528 NewBuf->BufUsed = CopySize;
531 NewBuf->buf[0] = '\0';
534 NewBuf->ConstBuf = 0;
542 * @ingroup StrBuf_DeConstructors
543 * @brief Set an existing buffer from a c-string
544 * @param Buf buffer to load
545 * @param ptr c-string to put into
546 * @param nChars set to -1 if we should work 0-terminated
547 * @returns the new length of the string
549 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
564 CopySize = strlen(ptr);
568 while ((Siz <= CopySize) && (Siz != 0))
576 if (Siz != Buf->BufSize)
577 IncreaseBuf(Buf, 0, Siz);
578 memcpy(Buf->buf, ptr, CopySize);
579 Buf->buf[CopySize] = '\0';
580 Buf->BufUsed = CopySize;
587 * @ingroup StrBuf_DeConstructors
588 * @brief use strbuf as wrapper for a string constant for easy handling
589 * @param StringConstant a string to wrap
590 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
592 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
596 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
597 NewBuf->buf = (char*) StringConstant;
598 NewBuf->BufSize = SizeOfStrConstant;
599 NewBuf->BufUsed = SizeOfStrConstant;
600 NewBuf->ConstBuf = 1;
609 * @ingroup StrBuf_DeConstructors
610 * @brief flush the content of a Buf; keep its struct
611 * @param buf Buffer to flush
613 int FlushStrBuf(StrBuf *buf)
615 if ((buf == NULL) || (buf->buf == NULL))
625 * @ingroup StrBuf_DeConstructors
626 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
627 * @param buf Buffer to wipe
629 int FLUSHStrBuf(StrBuf *buf)
635 if (buf->BufUsed > 0) {
636 memset(buf->buf, 0, buf->BufUsed);
643 int hFreeDbglog = -1;
646 * @ingroup StrBuf_DeConstructors
647 * @brief Release a Buffer
648 * Its a double pointer, so it can NULL your pointer
649 * so fancy SIG11 appear instead of random results
650 * @param FreeMe Pointer Pointer to the buffer to free
652 void FreeStrBuf (StrBuf **FreeMe)
657 dbg_FreeStrBuf(FreeMe, 'F');
659 if (!(*FreeMe)->ConstBuf)
660 free((*FreeMe)->buf);
666 * @ingroup StrBuf_DeConstructors
667 * @brief flatten a Buffer to the Char * we return
668 * Its a double pointer, so it can NULL your pointer
669 * so fancy SIG11 appear instead of random results
670 * The Callee then owns the buffer and is responsible for freeing it.
671 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
672 * @returns the pointer of the buffer; Callee owns the memory thereafter.
674 char *SmashStrBuf (StrBuf **SmashMe)
678 if ((SmashMe == NULL) || (*SmashMe == NULL))
681 dbg_FreeStrBuf(SmashMe, 'S');
683 Ret = (*SmashMe)->buf;
690 * @ingroup StrBuf_DeConstructors
691 * @brief Release the buffer
692 * If you want put your StrBuf into a Hash, use this as Destructor.
693 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
695 void HFreeStrBuf (void *VFreeMe)
697 StrBuf *FreeMe = (StrBuf*)VFreeMe;
701 dbg_FreeStrBuf(SmashMe, 'H');
703 if (!FreeMe->ConstBuf)
709 /*******************************************************************************
710 * Simple string transformations *
711 *******************************************************************************/
715 * @brief Wrapper around atol
717 long StrTol(const StrBuf *Buf)
722 return atol(Buf->buf);
729 * @brief Wrapper around atoi
731 int StrToi(const StrBuf *Buf)
735 if (Buf->BufUsed > 0)
736 return atoi(Buf->buf);
743 * @brief Checks to see if the string is a pure number
744 * @param Buf The buffer to inspect
745 * @returns 1 if its a pure number, 0, if not.
747 int StrBufIsNumber(const StrBuf *Buf) {
749 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
752 strtoll(Buf->buf, &pEnd, 10);
753 if (pEnd == Buf->buf)
755 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
757 if (Buf->buf == pEnd)
763 * @ingroup StrBuf_Filler
764 * @brief modifies a Single char of the Buf
765 * You can point to it via char* or a zero-based integer
766 * @param Buf The buffer to manipulate
767 * @param ptr char* to zero; use NULL if unused
768 * @param nThChar zero based pointer into the string; use -1 if unused
769 * @param PeekValue The Character to place into the position
771 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
776 nThChar = ptr - Buf->buf;
777 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
779 Buf->buf[nThChar] = PeekValue;
784 * @ingroup StrBuf_Filler
785 * @brief modifies a range of chars of the Buf
786 * You can point to it via char* or a zero-based integer
787 * @param Buf The buffer to manipulate
788 * @param ptr char* to zero; use NULL if unused
789 * @param nThChar zero based pointer into the string; use -1 if unused
790 * @param nChars how many chars are to be flushed?
791 * @param PookValue The Character to place into that area
793 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
798 nThChar = ptr - Buf->buf;
799 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
801 if (nThChar + nChars > Buf->BufUsed)
802 nChars = Buf->BufUsed - nThChar;
804 memset(Buf->buf + nThChar, PookValue, nChars);
805 /* just to be shure... */
806 Buf->buf[Buf->BufUsed] = 0;
811 * @ingroup StrBuf_Filler
812 * @brief Append a StringBuffer to the buffer
813 * @param Buf Buffer to modify
814 * @param AppendBuf Buffer to copy at the end of our buffer
815 * @param Offset Should we start copying from an offset?
817 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
819 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
820 (Buf == NULL) || (Buf->buf == NULL))
823 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
826 AppendBuf->BufUsed + Buf->BufUsed);
828 memcpy(Buf->buf + Buf->BufUsed,
829 AppendBuf->buf + Offset,
830 AppendBuf->BufUsed - Offset);
831 Buf->BufUsed += AppendBuf->BufUsed - Offset;
832 Buf->buf[Buf->BufUsed] = '\0';
837 * @ingroup StrBuf_Filler
838 * @brief Append a C-String to the buffer
839 * @param Buf Buffer to modify
840 * @param AppendBuf Buffer to copy at the end of our buffer
841 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
842 * @param Offset Should we start copying from an offset?
844 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
847 long BufSizeRequired;
849 if ((AppendBuf == NULL) || (Buf == NULL))
853 aps = strlen(AppendBuf + Offset);
855 aps = AppendSize - Offset;
857 BufSizeRequired = Buf->BufUsed + aps + 1;
858 if (Buf->BufSize <= BufSizeRequired)
859 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
861 memcpy(Buf->buf + Buf->BufUsed,
865 Buf->buf[Buf->BufUsed] = '\0';
869 * @ingroup StrBuf_Filler
870 * @brief sprintf like function appending the formated string to the buffer
871 * vsnprintf version to wrap into own calls
872 * @param Buf Buffer to extend by format and Params
873 * @param format printf alike format to add
874 * @param ap va_list containing the items for format
876 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
884 if ((Buf == NULL) || (format == NULL))
887 BufSize = Buf->BufSize;
888 nWritten = Buf->BufSize + 1;
889 Offset = Buf->BufUsed;
890 newused = Offset + nWritten;
892 while (newused >= BufSize) {
894 nWritten = vsnprintf(Buf->buf + Offset,
895 Buf->BufSize - Offset,
898 newused = Offset + nWritten;
899 if (newused >= Buf->BufSize) {
900 if (IncreaseBuf(Buf, 1, newused) == -1)
901 return; /* TODO: error handling? */
902 newused = Buf->BufSize + 1;
905 Buf->BufUsed = Offset + nWritten;
906 BufSize = Buf->BufSize;
913 * @ingroup StrBuf_Filler
914 * @brief sprintf like function appending the formated string to the buffer
915 * @param Buf Buffer to extend by format and Params
916 * @param format printf alike format to add
918 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
926 if ((Buf == NULL) || (format == NULL))
929 BufSize = Buf->BufSize;
930 nWritten = Buf->BufSize + 1;
931 Offset = Buf->BufUsed;
932 newused = Offset + nWritten;
934 while (newused >= BufSize) {
935 va_start(arg_ptr, format);
936 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
937 Buf->BufSize - Buf->BufUsed,
940 newused = Buf->BufUsed + nWritten;
941 if (newused >= Buf->BufSize) {
942 if (IncreaseBuf(Buf, 1, newused) == -1)
943 return; /* TODO: error handling? */
944 newused = Buf->BufSize + 1;
947 Buf->BufUsed += nWritten;
948 BufSize = Buf->BufSize;
955 * @ingroup StrBuf_Filler
956 * @brief sprintf like function putting the formated string into the buffer
957 * @param Buf Buffer to extend by format and Parameters
958 * @param format printf alike format to add
960 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
965 if ((Buf == NULL) || (format == NULL))
968 nWritten = Buf->BufSize + 1;
969 while (nWritten >= Buf->BufSize) {
970 va_start(arg_ptr, format);
971 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
973 if (nWritten >= Buf->BufSize) {
974 if (IncreaseBuf(Buf, 0, 0) == -1)
975 return; /* TODO: error handling? */
976 nWritten = Buf->BufSize + 1;
979 Buf->BufUsed = nWritten ;
984 * @ingroup StrBuf_Filler
985 * @brief Callback for cURL to append the webserver reply to a buffer
986 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
987 * @param size pre-defined by the cURL API; see man 3 curl for mre info
988 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
989 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
991 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1000 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1001 return size * nmemb;
1007 * @brief extracts a substring from Source into dest
1008 * @param dest buffer to place substring into
1009 * @param Source string to copy substring from
1010 * @param Offset chars to skip from start
1011 * @param nChars number of chars to copy
1012 * @returns the number of chars copied; may be different from nChars due to the size of Source
1014 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1016 size_t NCharsRemain;
1017 if (Offset > Source->BufUsed)
1023 if (Offset + nChars < Source->BufUsed)
1025 if (nChars >= dest->BufSize)
1026 IncreaseBuf(dest, 0, nChars + 1);
1027 memcpy(dest->buf, Source->buf + Offset, nChars);
1028 dest->BufUsed = nChars;
1029 dest->buf[dest->BufUsed] = '\0';
1032 NCharsRemain = Source->BufUsed - Offset;
1033 if (NCharsRemain >= dest->BufSize)
1034 IncreaseBuf(dest, 0, NCharsRemain + 1);
1035 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1036 dest->BufUsed = NCharsRemain;
1037 dest->buf[dest->BufUsed] = '\0';
1038 return NCharsRemain;
1043 * @brief Cut nChars from the start of the string
1044 * @param Buf Buffer to modify
1045 * @param nChars how many chars should be skipped?
1047 void StrBufCutLeft(StrBuf *Buf, int nChars)
1049 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1050 if (nChars >= Buf->BufUsed) {
1054 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1055 Buf->BufUsed -= nChars;
1056 Buf->buf[Buf->BufUsed] = '\0';
1061 * @brief Cut the trailing n Chars from the string
1062 * @param Buf Buffer to modify
1063 * @param nChars how many chars should be trunkated?
1065 void StrBufCutRight(StrBuf *Buf, int nChars)
1067 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1070 if (nChars >= Buf->BufUsed) {
1074 Buf->BufUsed -= nChars;
1075 Buf->buf[Buf->BufUsed] = '\0';
1080 * @brief Cut the string after n Chars
1081 * @param Buf Buffer to modify
1082 * @param AfternChars after how many chars should we trunkate the string?
1083 * @param At if non-null and points inside of our string, cut it there.
1085 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1087 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1089 AfternChars = At - Buf->buf;
1092 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1094 Buf->BufUsed = AfternChars;
1095 Buf->buf[Buf->BufUsed] = '\0';
1101 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1102 * @param Buf the string to modify
1104 void StrBufTrim(StrBuf *Buf)
1107 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1109 while ((Buf->BufUsed > 0) &&
1110 isspace(Buf->buf[Buf->BufUsed - 1]))
1114 Buf->buf[Buf->BufUsed] = '\0';
1116 if (Buf->BufUsed == 0) return;
1118 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1121 if (delta > 0) StrBufCutLeft(Buf, delta);
1125 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1126 * @param Buf the string to modify
1128 void StrBufSpaceToBlank(StrBuf *Buf)
1132 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1135 pche = pch + Buf->BufUsed;
1144 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1150 if ((Buf == NULL) || (Buf->buf == NULL))
1152 pLeft = pBuff = Buf->buf;
1153 while (pBuff != NULL) {
1155 pBuff = strchr(pBuff, leftboundary);
1164 pRight = strchr(pBuff, rightboundary);
1166 StrBufCutAt(Buf, 0, pRight);
1168 StrBufCutLeft(Buf, pLeft - Buf->buf);
1173 * @ingroup StrBuf_Filler
1174 * @brief uppercase the contents of a buffer
1175 * @param Buf the buffer to translate
1177 void StrBufUpCase(StrBuf *Buf)
1181 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1184 pche = pch + Buf->BufUsed;
1185 while (pch < pche) {
1186 *pch = toupper(*pch);
1193 * @ingroup StrBuf_Filler
1194 * @brief lowercase the contents of a buffer
1195 * @param Buf the buffer to translate
1197 void StrBufLowerCase(StrBuf *Buf)
1201 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1204 pche = pch + Buf->BufUsed;
1205 while (pch < pche) {
1206 *pch = tolower(*pch);
1212 /*******************************************************************************
1213 * a tokenizer that kills, maims, and destroys *
1214 *******************************************************************************/
1217 * @ingroup StrBuf_Tokenizer
1218 * @brief Replace a token at a given place with a given length by another token with given length
1219 * @param Buf String where to work on
1220 * @param where where inside of the Buf is the search-token
1221 * @param HowLong How long is the token to be replaced
1222 * @param Repl Token to insert at 'where'
1223 * @param ReplLen Length of repl
1224 * @returns -1 if fail else length of resulting Buf
1226 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1227 const char *Repl, long ReplLen)
1230 if ((Buf == NULL) ||
1231 (where > Buf->BufUsed) ||
1232 (where + HowLong > Buf->BufUsed))
1235 if (where + ReplLen - HowLong > Buf->BufSize)
1236 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1239 memmove(Buf->buf + where + ReplLen,
1240 Buf->buf + where + HowLong,
1241 Buf->BufUsed - where - HowLong);
1243 memcpy(Buf->buf + where,
1246 Buf->BufUsed += ReplLen - HowLong;
1248 return Buf->BufUsed;
1252 * @ingroup StrBuf_Tokenizer
1253 * @brief Counts the numbmer of tokens in a buffer
1254 * @param source String to count tokens in
1255 * @param tok Tokenizer char to count
1256 * @returns numbers of tokenizer chars found
1258 int StrBufNum_tokens(const StrBuf *source, char tok)
1262 if ((source == NULL) || (source->BufUsed == 0))
1264 if ((source->BufUsed == 1) && (*source->buf == tok))
1268 pche = pch + source->BufUsed;
1279 * @ingroup StrBuf_Tokenizer
1280 * @brief a string tokenizer
1281 * @param Source StringBuffer to read into
1282 * @param parmnum n'th Parameter to remove
1283 * @param separator tokenizer character
1284 * @returns -1 if not found, else length of token.
1286 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1289 char *d, *s, *end; /* dest, source */
1292 /* Find desired @parameter */
1293 end = Source->buf + Source->BufUsed;
1295 while ((d <= end) &&
1298 /* End of string, bail! */
1303 if (*d == separator) {
1308 if ((d == NULL) || (d >= end))
1309 return 0; /* @Parameter not found */
1311 /* Find next @parameter */
1313 while ((s <= end) &&
1314 (*s && *s != separator))
1318 if (*s == separator)
1322 /* Hack and slash */
1327 memmove(d, s, Source->BufUsed - (s - Source->buf));
1328 Source->BufUsed += ReducedBy;
1329 Source->buf[Source->BufUsed] = '\0';
1331 else if (d == Source->buf) {
1333 Source->BufUsed = 0;
1337 Source->BufUsed += ReducedBy;
1348 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1350 const StrBuf Temp = {
1363 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1367 * @ingroup StrBuf_Tokenizer
1368 * @brief a string tokenizer
1369 * @param dest Destination StringBuffer
1370 * @param Source StringBuffer to read into
1371 * @param parmnum n'th Parameter to extract
1372 * @param separator tokenizer character
1373 * @returns -1 if not found, else length of token.
1375 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1377 const char *s, *e; //* source * /
1378 int len = 0; //* running total length of extracted string * /
1379 int current_token = 0; //* token currently being processed * /
1382 dest->buf[0] = '\0';
1388 if ((Source == NULL) || (Source->BufUsed ==0)) {
1392 e = s + Source->BufUsed;
1395 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1397 while ((s < e) && !IsEmptyStr(s)) {
1398 if (*s == separator) {
1401 if (len >= dest->BufSize) {
1402 dest->BufUsed = len;
1403 if (IncreaseBuf(dest, 1, -1) < 0) {
1408 if ( (current_token == parmnum) &&
1409 (*s != separator)) {
1410 dest->buf[len] = *s;
1413 else if (current_token > parmnum) {
1419 dest->buf[len] = '\0';
1420 dest->BufUsed = len;
1422 if (current_token < parmnum) {
1423 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1426 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1435 * @ingroup StrBuf_Tokenizer
1436 * @brief a string tokenizer to fetch an integer
1437 * @param Source String containing tokens
1438 * @param parmnum n'th Parameter to extract
1439 * @param separator tokenizer character
1440 * @returns 0 if not found, else integer representation of the token
1442 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1452 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1459 * @ingroup StrBuf_Tokenizer
1460 * @brief a string tokenizer to fetch a long integer
1461 * @param Source String containing tokens
1462 * @param parmnum n'th Parameter to extract
1463 * @param separator tokenizer character
1464 * @returns 0 if not found, else long integer representation of the token
1466 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1476 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1484 * @ingroup StrBuf_Tokenizer
1485 * @brief a string tokenizer to fetch an unsigned long
1486 * @param Source String containing tokens
1487 * @param parmnum n'th Parameter to extract
1488 * @param separator tokenizer character
1489 * @returns 0 if not found, else unsigned long representation of the token
1491 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1502 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1506 return (unsigned long) atol(pnum);
1515 * @ingroup StrBuf_NextTokenizer
1516 * @brief a string tokenizer; Bounds checker
1517 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1518 * @param Source our tokenbuffer
1519 * @param pStart the token iterator pointer to inspect
1520 * @returns whether the revolving pointer is inside of the search range
1522 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1524 if ((Source == NULL) ||
1525 (*pStart == StrBufNOTNULL) ||
1526 (Source->BufUsed == 0))
1530 if (*pStart == NULL)
1534 else if (*pStart > Source->buf + Source->BufUsed)
1538 else if (*pStart <= Source->buf)
1547 * @ingroup StrBuf_NextTokenizer
1548 * @brief a string tokenizer
1549 * @param dest Destination StringBuffer
1550 * @param Source StringBuffer to read into
1551 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1552 * @param separator tokenizer
1553 * @returns -1 if not found, else length of token.
1555 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1557 const char *s; /* source */
1558 const char *EndBuffer; /* end stop of source buffer */
1559 int current_token = 0; /* token currently being processed */
1560 int len = 0; /* running total length of extracted string */
1562 if ((Source == NULL) ||
1563 (Source->BufUsed == 0) )
1565 *pStart = StrBufNOTNULL;
1571 EndBuffer = Source->buf + Source->BufUsed;
1575 dest->buf[0] = '\0';
1580 *pStart = EndBuffer + 1;
1584 if (*pStart == NULL)
1586 *pStart = Source->buf; /* we're starting to examine this buffer. */
1588 else if ((*pStart < Source->buf) ||
1589 (*pStart > EndBuffer ) )
1591 return -1; /* no more tokens to find. */
1595 /* start to find the next token */
1596 while ((s <= EndBuffer) &&
1597 (current_token == 0) )
1599 if (*s == separator)
1601 /* we found the next token */
1605 if (len >= dest->BufSize)
1607 /* our Dest-buffer isn't big enough, increase it. */
1608 dest->BufUsed = len;
1610 if (IncreaseBuf(dest, 1, -1) < 0) {
1611 /* WHUT? no more mem? bail out. */
1618 if ( (current_token == 0 ) && /* are we in our target token? */
1619 (!IsEmptyStr(s) ) &&
1620 (separator != *s) ) /* don't copy the token itself */
1622 dest->buf[len] = *s; /* Copy the payload */
1623 ++len; /* remember the bigger size. */
1629 /* did we reach the end? */
1630 if ((s > EndBuffer)) {
1631 EndBuffer = StrBufNOTNULL;
1632 *pStart = EndBuffer;
1635 *pStart = s; /* remember the position for the next run */
1638 /* sanitize our extracted token */
1639 dest->buf[len] = '\0';
1640 dest->BufUsed = len;
1647 * @ingroup StrBuf_NextTokenizer
1648 * @brief a string tokenizer
1649 * @param Source StringBuffer to read from
1650 * @param pStart pointer to the end of the last token. Feed with NULL.
1651 * @param separator tokenizer character
1652 * @param nTokens number of tokens to fastforward over
1653 * @returns -1 if not found, else length of token.
1655 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1657 const char *s, *EndBuffer; //* source * /
1658 int len = 0; //* running total length of extracted string * /
1659 int current_token = 0; //* token currently being processed * /
1661 if ((Source == NULL) ||
1662 (Source->BufUsed ==0)) {
1666 return Source->BufUsed;
1668 if (*pStart == NULL)
1669 *pStart = Source->buf;
1671 EndBuffer = Source->buf + Source->BufUsed;
1673 if ((*pStart < Source->buf) ||
1674 (*pStart > EndBuffer)) {
1682 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1684 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1685 if (*s == separator) {
1688 if (current_token >= nTokens) {
1700 * @ingroup StrBuf_NextTokenizer
1701 * @brief a string tokenizer to fetch an integer
1702 * @param Source StringBuffer to read from
1703 * @param pStart Cursor on the tokenstring
1704 * @param separator tokenizer character
1705 * @returns 0 if not found, else integer representation of the token
1707 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1717 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1724 * @ingroup StrBuf_NextTokenizer
1725 * @brief a string tokenizer to fetch a long integer
1726 * @param Source StringBuffer to read from
1727 * @param pStart Cursor on the tokenstring
1728 * @param separator tokenizer character
1729 * @returns 0 if not found, else long integer representation of the token
1731 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1741 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1749 * @ingroup StrBuf_NextTokenizer
1750 * @brief a string tokenizer to fetch an unsigned long
1751 * @param Source StringBuffer to read from
1752 * @param pStart Cursor on the tokenstring
1753 * @param separator tokenizer character
1754 * @returns 0 if not found, else unsigned long representation of the token
1756 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1767 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1771 return (unsigned long) atol(pnum);
1781 /*******************************************************************************
1782 * Escape Appending *
1783 *******************************************************************************/
1786 * @ingroup StrBuf_DeEnCoder
1787 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1788 * @param OutBuf the output buffer
1789 * @param In Buffer to encode
1790 * @param PlainIn way in from plain old c strings
1792 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1794 const char *pch, *pche;
1798 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1800 if (PlainIn != NULL) {
1801 len = strlen(PlainIn);
1807 pche = pch + In->BufUsed;
1814 pt = OutBuf->buf + OutBuf->BufUsed;
1815 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1817 while (pch < pche) {
1819 IncreaseBuf(OutBuf, 1, -1);
1820 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1821 pt = OutBuf->buf + OutBuf->BufUsed;
1824 if((*pch >= 'a' && *pch <= 'z') ||
1825 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1826 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1827 (*pch == '!') || (*pch == '_') ||
1828 (*pch == ',') || (*pch == '.'))
1835 *(pt + 1) = HexList[(unsigned char)*pch][0];
1836 *(pt + 2) = HexList[(unsigned char)*pch][1];
1838 OutBuf->BufUsed += 3;
1846 * @ingroup StrBuf_DeEnCoder
1847 * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1848 * @param OutBuf the output buffer
1849 * @param In Buffer to encode
1850 * @param PlainIn way in from plain old c strings
1852 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1854 const char *pch, *pche;
1858 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1860 if (PlainIn != NULL) {
1861 len = strlen(PlainIn);
1867 pche = pch + In->BufUsed;
1874 pt = OutBuf->buf + OutBuf->BufUsed;
1875 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1877 while (pch < pche) {
1879 IncreaseBuf(OutBuf, 1, -1);
1880 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1881 pt = OutBuf->buf + OutBuf->BufUsed;
1884 if((*pch >= 'a' && *pch <= 'z') ||
1885 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1886 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1887 (*pch == '!') || (*pch == '_') ||
1888 (*pch == ',') || (*pch == '.'))
1895 *(pt + 1) = HexList[(unsigned char)*pch][0];
1896 *(pt + 2) = HexList[(unsigned char)*pch][1];
1898 OutBuf->BufUsed += 3;
1906 * @ingroup StrBuf_DeEnCoder
1907 * @brief append a string in hex encoding to the buffer
1908 * @param OutBuf the output buffer
1909 * @param In Buffer to encode
1910 * @param PlainIn way in from plain old c strings
1911 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1913 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1915 const unsigned char *pch, *pche;
1919 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1921 if (PlainIn != NULL) {
1923 len = strlen((const char*)PlainIn);
1930 pch = (const unsigned char*)In->buf;
1931 pche = pch + In->BufUsed;
1938 pt = OutBuf->buf + OutBuf->BufUsed;
1939 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1941 while (pch < pche) {
1943 IncreaseBuf(OutBuf, 1, -1);
1944 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1945 pt = OutBuf->buf + OutBuf->BufUsed;
1948 *pt = HexList[*pch][0];
1950 *pt = HexList[*pch][1];
1951 pt ++; pch ++; OutBuf->BufUsed += 2;
1957 * @ingroup StrBuf_DeEnCoder
1958 * @brief append a string in hex encoding to the buffer
1959 * @param OutBuf the output buffer
1960 * @param In Buffer to encode
1961 * @param PlainIn way in from plain old c strings
1963 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1965 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1969 * @ingroup StrBuf_DeEnCoder
1970 * @brief Append a string, escaping characters which have meaning in HTML.
1972 * @param Target target buffer
1973 * @param Source source buffer; set to NULL if you just have a C-String
1974 * @param PlainIn Plain-C string to append; set to NULL if unused
1975 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1976 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1977 * if set to 2, linebreaks are replaced by <br/>
1979 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1981 const char *aptr, *eiptr;
1985 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1988 if (PlainIn != NULL) {
1990 len = strlen(PlainIn);
1995 eiptr = aptr + Source->BufUsed;
1996 len = Source->BufUsed;
2002 bptr = Target->buf + Target->BufUsed;
2003 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2005 while (aptr < eiptr){
2007 IncreaseBuf(Target, 1, -1);
2008 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2009 bptr = Target->buf + Target->BufUsed;
2012 memcpy(bptr, "<", 4);
2014 Target->BufUsed += 4;
2016 else if (*aptr == '>') {
2017 memcpy(bptr, ">", 4);
2019 Target->BufUsed += 4;
2021 else if (*aptr == '&') {
2022 memcpy(bptr, "&", 5);
2024 Target->BufUsed += 5;
2026 else if (*aptr == '"') {
2027 memcpy(bptr, """, 6);
2029 Target->BufUsed += 6;
2031 else if (*aptr == '\'') {
2032 memcpy(bptr, "'", 5);
2034 Target->BufUsed += 5;
2036 else if (*aptr == LB) {
2041 else if (*aptr == RB) {
2046 else if (*aptr == QU) {
2051 else if ((*aptr == 32) && (nbsp == 1)) {
2052 memcpy(bptr, " ", 6);
2054 Target->BufUsed += 6;
2056 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2057 *bptr='\0'; /* nothing */
2059 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2060 memcpy(bptr, "<br/>", 11);
2062 Target->BufUsed += 11;
2066 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2067 *bptr='\0'; /* nothing */
2077 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2079 return Target->BufUsed;
2083 * @ingroup StrBuf_DeEnCoder
2084 * @brief Append a string, escaping characters which have meaning in HTML.
2085 * Converts linebreaks into blanks; escapes single quotes
2086 * @param Target target buffer
2087 * @param Source source buffer; set to NULL if you just have a C-String
2088 * @param PlainIn Plain-C string to append; set to NULL if unused
2090 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2092 const char *aptr, *eiptr;
2096 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2099 if (PlainIn != NULL) {
2101 len = strlen(PlainIn);
2106 eiptr = aptr + Source->BufUsed;
2107 len = Source->BufUsed;
2113 eptr = Target->buf + Target->BufSize - 8;
2114 tptr = Target->buf + Target->BufUsed;
2116 while (aptr < eiptr){
2118 IncreaseBuf(Target, 1, -1);
2119 eptr = Target->buf + Target->BufSize - 8;
2120 tptr = Target->buf + Target->BufUsed;
2123 if (*aptr == '\n') {
2127 else if (*aptr == '\r') {
2131 else if (*aptr == '\'') {
2137 Target->BufUsed += 5;
2150 * @ingroup StrBuf_DeEnCoder
2151 * @brief Append a string, escaping characters which have meaning in ICAL.
2153 * @param Target target buffer
2154 * @param Source source buffer; set to NULL if you just have a C-String
2155 * @param PlainIn Plain-C string to append; set to NULL if unused
2157 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2159 const char *aptr, *eiptr;
2163 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2166 if (PlainIn != NULL) {
2168 len = strlen(PlainIn);
2173 eiptr = aptr + Source->BufUsed;
2174 len = Source->BufUsed;
2180 eptr = Target->buf + Target->BufSize - 8;
2181 tptr = Target->buf + Target->BufUsed;
2183 while (aptr < eiptr){
2184 if(tptr + 3 >= eptr) {
2185 IncreaseBuf(Target, 1, -1);
2186 eptr = Target->buf + Target->BufSize - 8;
2187 tptr = Target->buf + Target->BufUsed;
2190 if (*aptr == '\n') {
2197 else if (*aptr == '\r') {
2204 else if (*aptr == ',') {
2220 * @ingroup StrBuf_DeEnCoder
2221 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
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
2226 * @returns size of result or -1
2228 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2230 const char *aptr, *eiptr;
2235 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2238 if (PlainIn != NULL) {
2240 len = strlen(PlainIn);
2245 eiptr = aptr + Source->BufUsed;
2246 len = Source->BufUsed;
2252 bptr = Target->buf + Target->BufUsed;
2253 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2255 while (aptr < eiptr){
2257 IncreaseBuf(Target, 1, -1);
2258 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2259 bptr = Target->buf + Target->BufUsed;
2263 memcpy(bptr, HKEY("\\n"));
2265 Target->BufUsed += 2;
2268 memcpy(bptr, HKEY("\\r"));
2270 Target->BufUsed += 2;
2277 Target->BufUsed += 2;
2280 if ((*(aptr + 1) == 'u') &&
2281 isxdigit(*(aptr + 2)) &&
2282 isxdigit(*(aptr + 3)) &&
2283 isxdigit(*(aptr + 4)) &&
2284 isxdigit(*(aptr + 5)))
2285 { /* oh, a unicode escaper. let it pass through. */
2286 memcpy(bptr, aptr, 6);
2289 Target->BufUsed += 6;
2297 Target->BufUsed += 2;
2305 Target->BufUsed += 2;
2312 Target->BufUsed += 2;
2319 Target->BufUsed += 2;
2322 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2323 while (IsUtf8Sequence > 0){
2326 if (--IsUtf8Sequence)
2334 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2336 return Target->BufUsed;
2340 * @ingroup StrBuf_DeEnCoder
2341 * @brief Append a string, escaping characters which have meaning in HTML + json.
2343 * @param Target target buffer
2344 * @param Source source buffer; set to NULL if you just have a C-String
2345 * @param PlainIn Plain-C string to append; set to NULL if unused
2346 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2347 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2348 * if set to 2, linebreaks are replaced by <br/>
2350 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2352 const char *aptr, *eiptr;
2355 int IsUtf8Sequence = 0;
2357 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2360 if (PlainIn != NULL) {
2362 len = strlen(PlainIn);
2367 eiptr = aptr + Source->BufUsed;
2368 len = Source->BufUsed;
2374 bptr = Target->buf + Target->BufUsed;
2375 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2377 while (aptr < eiptr){
2379 IncreaseBuf(Target, 1, -1);
2380 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2381 bptr = Target->buf + Target->BufUsed;
2385 memcpy(bptr, HKEY("<"));
2387 Target->BufUsed += 4;
2390 memcpy(bptr, HKEY(">"));
2392 Target->BufUsed += 4;
2395 memcpy(bptr, HKEY("&"));
2397 Target->BufUsed += 5;
2410 switch (nolinebreaks) {
2412 *bptr='\0'; /* nothing */
2415 memcpy(bptr, HKEY("<br/>"));
2417 Target->BufUsed += 11;
2420 memcpy(bptr, HKEY("\\n"));
2422 Target->BufUsed += 2;
2426 switch (nolinebreaks) {
2429 *bptr='\0'; /* nothing */
2432 memcpy(bptr, HKEY("\\r"));
2434 Target->BufUsed += 2;
2444 Target->BufUsed += 2;
2447 if ((*(aptr + 1) == 'u') &&
2448 isxdigit(*(aptr + 2)) &&
2449 isxdigit(*(aptr + 3)) &&
2450 isxdigit(*(aptr + 4)) &&
2451 isxdigit(*(aptr + 5)))
2452 { /* oh, a unicode escaper. let it pass through. */
2453 memcpy(bptr, aptr, 6);
2456 Target->BufUsed += 6;
2464 Target->BufUsed += 2;
2472 Target->BufUsed += 2;
2479 Target->BufUsed += 2;
2486 Target->BufUsed += 2;
2490 memcpy(bptr, HKEY(" "));
2492 Target->BufUsed += 6;
2496 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2497 while (IsUtf8Sequence > 0){
2500 if (--IsUtf8Sequence)
2508 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2510 return Target->BufUsed;
2514 * @ingroup StrBuf_DeEnCoder
2515 * @brief unhide special chars hidden to the HTML escaper
2516 * @param target buffer to put the unescaped string in
2517 * @param source buffer to unescape
2519 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2524 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2530 FlushStrBuf(target);
2532 len = source->BufUsed;
2533 for (a = 0; a < len; ++a) {
2534 if (target->BufUsed >= target->BufSize)
2535 IncreaseBuf(target, 1, -1);
2537 if (source->buf[a] == '=') {
2538 hex[0] = source->buf[a + 1];
2539 hex[1] = source->buf[a + 2];
2542 sscanf(hex, "%02x", &b);
2543 target->buf[target->BufUsed] = b;
2544 target->buf[++target->BufUsed] = 0;
2548 target->buf[target->BufUsed] = source->buf[a];
2549 target->buf[++target->BufUsed] = 0;
2556 * @ingroup StrBuf_DeEnCoder
2557 * @brief hide special chars from the HTML escapers and friends
2558 * @param target buffer to put the escaped string in
2559 * @param source buffer to escape
2561 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2566 FlushStrBuf(target);
2568 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2573 len = source->BufUsed;
2574 for (i=0; i<len; ++i) {
2575 if (target->BufUsed + 4 >= target->BufSize)
2576 IncreaseBuf(target, 1, -1);
2577 if ( (isalnum(source->buf[i])) ||
2578 (source->buf[i]=='-') ||
2579 (source->buf[i]=='_') ) {
2580 target->buf[target->BufUsed++] = source->buf[i];
2583 sprintf(&target->buf[target->BufUsed],
2585 (0xFF &source->buf[i]));
2586 target->BufUsed += 3;
2589 target->buf[target->BufUsed + 1] = '\0';
2593 /*******************************************************************************
2594 * Quoted Printable de/encoding *
2595 *******************************************************************************/
2598 * @ingroup StrBuf_DeEnCoder
2599 * @brief decode a buffer from base 64 encoding; destroys original
2600 * @param Buf Buffor to transform
2602 int StrBufDecodeBase64(StrBuf *Buf)
2606 if (Buf == NULL) return -1;
2608 xferbuf = (char*) malloc(Buf->BufSize);
2610 siz = CtdlDecodeBase64(xferbuf,
2620 * @ingroup StrBuf_DeEnCoder
2621 * @brief decode a buffer from base 64 encoding; destroys original
2622 * @param Buf Buffor to transform
2624 int StrBufDecodeHex(StrBuf *Buf)
2627 char *pch, *pche, *pchi;
2629 if (Buf == NULL) return -1;
2631 pch = pchi = Buf->buf;
2632 pche = pch + Buf->BufUsed;
2634 while (pchi < pche){
2635 ch = decode_hex(pchi);
2642 Buf->BufUsed = pch - Buf->buf;
2643 return Buf->BufUsed;
2647 * @ingroup StrBuf_DeEnCoder
2648 * @brief replace all chars >0x20 && < 0x7F with Mute
2649 * @param Mute char to put over invalid chars
2650 * @param Buf Buffor to transform
2652 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2656 if (Buf == NULL) return -1;
2657 pch = (unsigned char *)Buf->buf;
2658 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2659 if ((*pch < 0x20) || (*pch > 0x7F))
2663 return Buf->BufUsed;
2668 * @ingroup StrBuf_DeEnCoder
2669 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2670 * @param Buf Buffer to translate
2671 * @param StripBlanks Reduce several blanks to one?
2673 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2682 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2683 Buf->buf[Buf->BufUsed - 1] = '\0';
2688 while (a < Buf->BufUsed) {
2689 if (Buf->buf[a] == '+')
2691 else if (Buf->buf[a] == '%') {
2692 /* don't let % chars through, rather truncate the input. */
2693 if (a + 2 > Buf->BufUsed) {
2698 hex[0] = Buf->buf[a + 1];
2699 hex[1] = Buf->buf[a + 2];
2702 sscanf(hex, "%02x", &b);
2703 Buf->buf[a] = (char) b;
2704 len = Buf->BufUsed - a - 2;
2706 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2718 * @ingroup StrBuf_DeEnCoder
2719 * @brief RFC2047-encode a header field if necessary.
2720 * If no non-ASCII characters are found, the string
2721 * will be copied verbatim without encoding.
2723 * @param target Target buffer.
2724 * @param source Source string to be encoded.
2725 * @returns encoded length; -1 if non success.
2727 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2729 const char headerStr[] = "=?UTF-8?Q?";
2730 int need_to_encode = 0;
2734 if ((source == NULL) ||
2738 while ((i < source->BufUsed) &&
2739 (!IsEmptyStr (&source->buf[i])) &&
2740 (need_to_encode == 0)) {
2741 if (((unsigned char) source->buf[i] < 32) ||
2742 ((unsigned char) source->buf[i] > 126)) {
2748 if (!need_to_encode) {
2749 if (*target == NULL) {
2750 *target = NewStrBufPlain(source->buf, source->BufUsed);
2753 FlushStrBuf(*target);
2754 StrBufAppendBuf(*target, source, 0);
2757 return (*target)->BufUsed;
2761 if (*target == NULL)
2762 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2763 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2764 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2765 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2766 (*target)->BufUsed = sizeof(headerStr) - 1;
2767 for (i=0; (i < source->BufUsed); ++i) {
2768 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2769 IncreaseBuf(*target, 1, 0);
2770 ch = (unsigned char) source->buf[i];
2780 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2781 (*target)->BufUsed += 3;
2785 (*target)->buf[(*target)->BufUsed] = '_';
2787 (*target)->buf[(*target)->BufUsed] = ch;
2788 (*target)->BufUsed++;
2792 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2793 IncreaseBuf(*target, 1, 0);
2795 (*target)->buf[(*target)->BufUsed++] = '?';
2796 (*target)->buf[(*target)->BufUsed++] = '=';
2797 (*target)->buf[(*target)->BufUsed] = '\0';
2798 return (*target)->BufUsed;;
2803 static void AddRecipient(StrBuf *Target,
2805 StrBuf *EmailAddress,
2810 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2811 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2813 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2814 StrBufRFC2047encode(&EncBuf, UserName);
2815 StrBufAppendBuf(Target, EncBuf, 0);
2816 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2817 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2819 if (StrLength(EmailAddress) > 0){
2820 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2821 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2822 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2828 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2829 * \param Recp Source list of email recipients
2830 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2831 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2832 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2833 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2835 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2837 StrBuf *EmailAddress,
2841 const char *pch, *pche;
2842 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2844 if ((Recp == NULL) || (StrLength(Recp) == 0))
2848 pche = pch + StrLength(Recp);
2850 if (!CheckEncode(pch, -1, pche))
2851 return NewStrBufDup(Recp);
2853 Target = NewStrBufPlain(NULL, StrLength(Recp));
2855 while ((pch != NULL) && (pch < pche))
2857 while (isspace(*pch)) pch++;
2858 UserEnd = EmailStart = EmailEnd = NULL;
2860 if ((*pch == '"') || (*pch == '\'')) {
2861 UserStart = pch + 1;
2863 UserEnd = strchr(UserStart, *pch);
2864 if (UserEnd == NULL)
2865 break; ///TODO: Userfeedback??
2866 EmailStart = UserEnd + 1;
2867 while (isspace(*EmailStart))
2869 if (UserEnd == UserStart) {
2870 UserStart = UserEnd = NULL;
2873 if (*EmailStart == '<') {
2875 EmailEnd = strchr(EmailStart, '>');
2876 if (EmailEnd == NULL)
2877 EmailEnd = strchr(EmailStart, ',');
2881 EmailEnd = strchr(EmailStart, ',');
2883 if (EmailEnd == NULL)
2890 EmailEnd = strchr(UserStart, ',');
2891 if (EmailEnd == NULL) {
2892 EmailEnd = strchr(pch, '>');
2894 if (EmailEnd != NULL) {
2904 while ((EmailEnd > UserStart) && !gt &&
2905 ((*EmailEnd == ',') ||
2906 (*EmailEnd == '>') ||
2907 (isspace(*EmailEnd))))
2909 if (*EmailEnd == '>')
2914 if (EmailEnd == UserStart)
2918 EmailStart = strchr(UserStart, '<');
2919 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2921 UserEnd = EmailStart;
2923 while ((UserEnd > UserStart) &&
2924 isspace (*(UserEnd - 1)))
2927 if (UserStart >= UserEnd)
2928 UserStart = UserEnd = NULL;
2930 else { /* this is a local recipient... no domain, just a realname */
2931 EmailStart = UserStart;
2932 At = strchr(EmailStart, '@');
2938 EmailStart = UserStart;
2944 if ((UserStart != NULL) && (UserEnd != NULL))
2945 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2946 else if ((UserStart != NULL) && (UserEnd == NULL))
2947 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2949 FlushStrBuf(UserName);
2951 if ((EmailStart != NULL) && (EmailEnd != NULL))
2952 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2953 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2954 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2956 FlushStrBuf(EmailAddress);
2958 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2963 if ((pch != NULL) && (*pch == ','))
2965 if (pch != NULL) while (isspace(*pch))
2974 * @brief replaces all occurances of 'search' by 'replace'
2975 * @param buf Buffer to modify
2976 * @param search character to search
2977 * @param replace character to replace search by
2979 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2984 for (i=0; i<buf->BufUsed; i++)
2985 if (buf->buf[i] == search)
2986 buf->buf[i] = replace;
2992 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2993 * @param buf Buffer to modify
2995 void StrBufToUnixLF(StrBuf *buf)
2997 char *pche, *pchS, *pchT;
3001 pche = buf->buf + buf->BufUsed;
3002 pchS = pchT = buf->buf;
3008 if (*pchS != '\n') {
3017 buf->BufUsed = pchT - buf->buf;
3021 /*******************************************************************************
3022 * Iconv Wrapper; RFC822 de/encoding *
3023 *******************************************************************************/
3026 * @ingroup StrBuf_DeEnCoder
3027 * @brief Wrapper around iconv_open()
3028 * Our version adds aliases for non-standard Microsoft charsets
3029 * such as 'MS950', aliasing them to names like 'CP950'
3031 * @param tocode Target encoding
3032 * @param fromcode Source encoding
3033 * @param pic anonimized pointer to iconv struct
3035 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3038 iconv_t ic = (iconv_t)(-1) ;
3039 ic = iconv_open(tocode, fromcode);
3040 if (ic == (iconv_t)(-1) ) {
3041 char alias_fromcode[64];
3042 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3043 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3044 alias_fromcode[0] = 'C';
3045 alias_fromcode[1] = 'P';
3046 ic = iconv_open(tocode, alias_fromcode);
3049 *(iconv_t *)pic = ic;
3055 * @ingroup StrBuf_DeEnCoder
3056 * @brief find one chunk of a RFC822 encoded string
3057 * @param Buffer where to search
3058 * @param bptr where to start searching
3059 * @returns found position, NULL if none.
3061 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3064 /* Find the next ?Q? */
3065 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3068 end = strchr(bptr + 2, '?');
3073 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3074 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3075 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3076 (*(end + 2) == '?')) {
3077 /* skip on to the end of the cluster, the next ?= */
3078 end = strstr(end + 3, "?=");
3081 /* sort of half valid encoding, try to find an end. */
3082 end = strstr(bptr, "?=");
3089 * @ingroup StrBuf_DeEnCoder
3090 * @brief convert one buffer according to the preselected iconv pointer PIC
3091 * @param ConvertBuf buffer we need to translate
3092 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3093 * @param pic Pointer to the iconv-session Object
3095 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3101 char *ibuf; /**< Buffer of characters to be converted */
3102 char *obuf; /**< Buffer for converted characters */
3103 size_t ibuflen; /**< Length of input buffer */
3104 size_t obuflen; /**< Length of output buffer */
3107 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3110 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3111 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3112 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3114 ic = *(iconv_t*)pic;
3115 ibuf = ConvertBuf->buf;
3116 ibuflen = ConvertBuf->BufUsed;
3118 obuflen = TmpBuf->BufSize;
3120 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3123 if (errno == E2BIG) {
3125 IncreaseBuf(TmpBuf, 0, 0);
3130 else if (errno == EILSEQ){
3131 /* hm, invalid utf8 sequence... what to do now? */
3132 /* An invalid multibyte sequence has been encountered in the input */
3134 else if (errno == EINVAL) {
3135 /* An incomplete multibyte sequence has been encountered in the input. */
3138 FlushStrBuf(TmpBuf);
3141 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3142 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3144 /* little card game: wheres the red lady? */
3145 SwapBuffers(ConvertBuf, TmpBuf);
3146 FlushStrBuf(TmpBuf);
3153 * @ingroup StrBuf_DeEnCoder
3154 * @brief catches one RFC822 encoded segment, and decodes it.
3155 * @param Target buffer to fill with result
3156 * @param DecodeMe buffer with stuff to process
3157 * @param SegmentStart points to our current segment in DecodeMe
3158 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3159 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3160 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3161 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3163 inline static void DecodeSegment(StrBuf *Target,
3164 const StrBuf *DecodeMe,
3165 const char *SegmentStart,
3166 const char *SegmentEnd,
3168 StrBuf *ConvertBuf2,
3169 StrBuf *FoundCharset)
3175 iconv_t ic = (iconv_t)(-1);
3179 /* Now we handle foreign character sets properly encoded
3180 * in RFC2047 format.
3182 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3183 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3184 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3185 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3186 if (FoundCharset != NULL) {
3187 FlushStrBuf(FoundCharset);
3188 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3190 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3191 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3193 *encoding = toupper(*encoding);
3194 if (*encoding == 'B') { /**< base64 */
3195 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3196 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3197 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3199 ConvertBuf->BufUsed);
3201 else if (*encoding == 'Q') { /**< quoted-printable */
3205 while (pos < ConvertBuf->BufUsed)
3207 if (ConvertBuf->buf[pos] == '_')
3208 ConvertBuf->buf[pos] = ' ';
3212 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3213 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3215 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3218 ConvertBuf->BufUsed);
3221 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3224 ctdl_iconv_open("UTF-8", charset, &ic);
3225 if (ic != (iconv_t)(-1) ) {
3227 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3228 StrBufAppendBuf(Target, ConvertBuf2, 0);
3233 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3239 * @ingroup StrBuf_DeEnCoder
3240 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3241 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3242 * @param Target where to put the decoded string to
3243 * @param DecodeMe buffer with encoded string
3244 * @param DefaultCharset if we don't find one, which should we use?
3245 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3246 * put it here for later use where no string might be known.
3248 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3251 StrBuf *ConvertBuf2;
3252 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3253 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3255 StrBuf_RFC822_2_Utf8(Target,
3261 FreeStrBuf(&ConvertBuf);
3262 FreeStrBuf(&ConvertBuf2);
3266 * @ingroup StrBuf_DeEnCoder
3267 * @brief Handle subjects with RFC2047 encoding such as:
3268 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3269 * @param Target where to put the decoded string to
3270 * @param DecodeMe buffer with encoded string
3271 * @param DefaultCharset if we don't find one, which should we use?
3272 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3273 * put it here for later use where no string might be known.
3274 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3275 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3277 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3278 const StrBuf *DecodeMe,
3279 const StrBuf* DefaultCharset,
3280 StrBuf *FoundCharset,
3282 StrBuf *ConvertBuf2)
3284 StrBuf *DecodedInvalidBuf = NULL;
3285 const StrBuf *DecodeMee = DecodeMe;
3286 const char *start, *end, *next, *nextend, *ptr = NULL;
3288 iconv_t ic = (iconv_t)(-1) ;
3293 int illegal_non_rfc2047_encoding = 0;
3296 if (DecodeMe == NULL)
3298 /* Sometimes, badly formed messages contain strings which were simply
3299 * written out directly in some foreign character set instead of
3300 * using RFC2047 encoding. This is illegal but we will attempt to
3301 * handle it anyway by converting from a user-specified default
3302 * charset to UTF-8 if we see any nonprintable characters.
3305 for (i=0; i<DecodeMe->BufUsed; ++i) {
3306 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3307 illegal_non_rfc2047_encoding = 1;
3312 if ((illegal_non_rfc2047_encoding) &&
3313 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3314 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3317 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3318 if (ic != (iconv_t)(-1) ) {
3319 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3320 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3321 DecodeMee = DecodedInvalidBuf;
3327 /* pre evaluate the first pair */
3329 start = strstr(DecodeMee->buf, "=?");
3330 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3332 end = FindNextEnd (DecodeMee, start + 2);
3334 StrBufAppendBuf(Target, DecodeMee, 0);
3335 FreeStrBuf(&DecodedInvalidBuf);
3340 if (start != DecodeMee->buf) {
3343 nFront = start - DecodeMee->buf;
3344 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3347 * Since spammers will go to all sorts of absurd lengths to get their
3348 * messages through, there are LOTS of corrupt headers out there.
3349 * So, prevent a really badly formed RFC2047 header from throwing
3350 * this function into an infinite loop.
3352 while ((start != NULL) &&
3359 DecodeSegment(Target,
3367 next = strstr(end, "=?");
3369 if ((next != NULL) &&
3371 nextend = FindNextEnd(DecodeMee, next);
3372 if (nextend == NULL)
3375 /* did we find two partitions */
3376 if ((next != NULL) &&
3380 while ((ptr < next) &&
3387 * did we find a gab just filled with blanks?
3388 * if not, copy its stuff over.
3392 StrBufAppendBufPlain(Target,
3398 /* our next-pair is our new first pair now. */
3404 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3405 if ((end != NULL) && (end < nextend)) {
3407 while ( (ptr < nextend) &&
3414 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3416 FreeStrBuf(&DecodedInvalidBuf);
3419 /*******************************************************************************
3420 * Manipulating UTF-8 Strings *
3421 *******************************************************************************/
3425 * @brief evaluate the length of an utf8 special character sequence
3426 * @param Char the character to examine
3427 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3429 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3432 unsigned char test = (1<<7);
3434 if ((*CharS & 0xC0) != 0xC0)
3438 ((test & ((unsigned char)*CharS)) != 0))
3443 if ((n > 6) || ((CharE - CharS) < n))
3450 * @brief detect whether this char starts an utf-8 encoded char
3451 * @param Char character to inspect
3452 * @returns yes or no
3454 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3456 /** 11??.???? indicates an UTF8 Sequence. */
3457 return ((Char & 0xC0) == 0xC0);
3462 * @brief measure the number of glyphs in an UTF8 string...
3463 * @param Buf string to measure
3464 * @returns the number of glyphs in Buf
3466 long StrBuf_Utf8StrLen(StrBuf *Buf)
3472 if ((Buf == NULL) || (Buf->BufUsed == 0))
3475 eptr = Buf->buf + Buf->BufUsed;
3476 while ((aptr < eptr) && (*aptr != '\0')) {
3477 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3478 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3479 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3492 * @brief cuts a string after maxlen glyphs
3493 * @param Buf string to cut to maxlen glyphs
3494 * @param maxlen how long may the string become?
3495 * @returns current length of the string
3497 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3503 eptr = Buf->buf + Buf->BufUsed;
3504 while ((aptr < eptr) && (*aptr != '\0')) {
3505 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3506 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3507 while ((*aptr++ != '\0') && (m-- > 0));
3516 Buf->BufUsed = aptr - Buf->buf;
3517 return Buf->BufUsed;
3520 return Buf->BufUsed;
3528 /*******************************************************************************
3530 *******************************************************************************/
3533 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3534 #define OS_CODE 0x03 /*< unix */
3537 * @ingroup StrBuf_DeEnCoder
3538 * @brief uses the same calling syntax as compress2(), but it
3539 * creates a stream compatible with HTTP "Content-encoding: gzip"
3540 * @param dest compressed buffer
3541 * @param destLen length of the compresed data
3542 * @param source source to encode
3543 * @param sourceLen length of source to encode
3544 * @param level compression level
3546 int ZEXPORT compress_gzip(Bytef * dest,
3548 const Bytef * source,
3552 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3554 /* write gzip header */
3555 snprintf((char *) dest, *destLen,
3556 "%c%c%c%c%c%c%c%c%c%c",
3557 gz_magic[0], gz_magic[1], Z_DEFLATED,
3558 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3561 /* normal deflate */
3564 stream.next_in = (Bytef *) source;
3565 stream.avail_in = (uInt) sourceLen;
3566 stream.next_out = dest + 10L; // after header
3567 stream.avail_out = (uInt) * destLen;
3568 if ((uLong) stream.avail_out != *destLen)
3571 stream.zalloc = (alloc_func) 0;
3572 stream.zfree = (free_func) 0;
3573 stream.opaque = (voidpf) 0;
3575 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3576 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3580 err = deflate(&stream, Z_FINISH);
3581 if (err != Z_STREAM_END) {
3582 deflateEnd(&stream);
3583 return err == Z_OK ? Z_BUF_ERROR : err;
3585 *destLen = stream.total_out + 10L;
3587 /* write CRC and Length */
3588 uLong crc = crc32(0L, source, sourceLen);
3590 for (n = 0; n < 4; ++n, ++*destLen) {
3591 dest[*destLen] = (int) (crc & 0xff);
3594 uLong len = stream.total_in;
3595 for (n = 0; n < 4; ++n, ++*destLen) {
3596 dest[*destLen] = (int) (len & 0xff);
3599 err = deflateEnd(&stream);
3606 * @ingroup StrBuf_DeEnCoder
3607 * @brief compress the buffer with gzip
3608 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3609 * @param Buf buffer whose content is to be gzipped
3611 int CompressBuffer(StrBuf *Buf)
3614 char *compressed_data = NULL;
3615 size_t compressed_len, bufsize;
3618 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3619 compressed_data = malloc(compressed_len);
3621 if (compressed_data == NULL)
3623 /* Flush some space after the used payload so valgrind shuts up... */
3624 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3625 Buf->buf[Buf->BufUsed + i++] = '\0';
3626 if (compress_gzip((Bytef *) compressed_data,
3629 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3632 Buf->buf = compressed_data;
3633 Buf->BufUsed = compressed_len;
3634 Buf->BufSize = bufsize;
3635 /* Flush some space after the used payload so valgrind shuts up... */
3637 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3638 Buf->buf[Buf->BufUsed + i++] = '\0';
3641 free(compressed_data);
3643 #endif /* HAVE_ZLIB */
3647 /*******************************************************************************
3648 * File I/O; Callbacks to libevent *
3649 *******************************************************************************/
3651 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3656 if ((FB == NULL) || (FB->Buf == NULL))
3660 * check whether the read pointer is somewhere in a range
3661 * where a cut left is inexpensive
3664 if (FB->ReadWritePointer != NULL)
3668 already_read = FB->ReadWritePointer - FB->Buf->buf;
3669 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3671 if (already_read != 0) {
3674 unread = FB->Buf->BufUsed - already_read;
3676 /* else nothing to compact... */
3678 FB->ReadWritePointer = FB->Buf->buf;
3679 bufremain = FB->Buf->BufSize;
3681 else if ((unread < 64) ||
3682 (bufremain < already_read))
3685 * if its just a tiny bit remaining, or we run out of space...
3688 FB->Buf->BufUsed = unread;
3689 if (unread < already_read)
3690 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3692 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3693 FB->ReadWritePointer = FB->Buf->buf;
3694 bufremain = FB->Buf->BufSize - unread - 1;
3696 else if (bufremain < (FB->Buf->BufSize / 10))
3698 /* get a bigger buffer */
3700 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3702 FB->ReadWritePointer = FB->Buf->buf + unread;
3704 bufremain = FB->Buf->BufSize - unread - 1;
3705 /*TODO: special increase function that won't copy the already read! */
3708 else if (bufremain < 10) {
3709 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3711 FB->ReadWritePointer = FB->Buf->buf;
3713 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3718 FB->ReadWritePointer = FB->Buf->buf;
3719 bufremain = FB->Buf->BufSize - 1;
3722 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3725 FB->Buf->BufUsed += n;
3726 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3731 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3736 if ((FB == NULL) || (FB->Buf == NULL))
3739 if (FB->ReadWritePointer != NULL)
3741 WriteRemain = FB->Buf->BufUsed -
3742 (FB->ReadWritePointer -
3746 FB->ReadWritePointer = FB->Buf->buf;
3747 WriteRemain = FB->Buf->BufUsed;
3750 n = write(fd, FB->ReadWritePointer, WriteRemain);
3752 FB->ReadWritePointer += n;
3754 if (FB->ReadWritePointer ==
3755 FB->Buf->buf + FB->Buf->BufUsed)
3757 FlushStrBuf(FB->Buf);
3758 FB->ReadWritePointer = NULL;
3761 // check whether we've got something to write
3762 // get the maximum chunk plus the pointer we can send
3763 // write whats there
3764 // if not all was sent, remember the send pointer for the next time
3765 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3771 * @ingroup StrBuf_IO
3772 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3773 * @param LineBuf your line will be copied here.
3774 * @param FB BLOB with lines of text...
3775 * @param Ptr moved arround to keep the next-line across several iterations
3776 * has to be &NULL on start; will be &NotNULL on end of buffer
3777 * @returns size of copied buffer
3779 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3781 const char *aptr, *ptr, *eptr;
3784 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3788 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3789 FB->ReadWritePointer = StrBufNOTNULL;
3793 FlushStrBuf(LineBuf);
3794 if (FB->ReadWritePointer == NULL)
3795 ptr = aptr = FB->Buf->buf;
3797 ptr = aptr = FB->ReadWritePointer;
3799 optr = LineBuf->buf;
3800 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3801 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3803 while ((ptr <= eptr) &&
3810 LineBuf->BufUsed = optr - LineBuf->buf;
3811 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3812 optr = LineBuf->buf + LineBuf->BufUsed;
3813 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3818 if (optr > LineBuf->buf)
3820 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3821 LineBuf->BufUsed = optr - LineBuf->buf;
3823 if ((FB->ReadWritePointer != NULL) &&
3824 (FB->ReadWritePointer != FB->Buf->buf))
3826 /* Ok, the client application read all the data
3827 it was interested in so far. Since there is more to read,
3828 we now shrink the buffer, and move the rest over.
3830 StrBufCutLeft(FB->Buf,
3831 FB->ReadWritePointer - FB->Buf->buf);
3832 FB->ReadWritePointer = FB->Buf->buf;
3834 return eMustReadMore;
3837 LineBuf->BufUsed = optr - LineBuf->buf;
3839 if ((ptr <= eptr) && (*ptr == '\r'))
3841 if ((ptr <= eptr) && (*ptr == '\n'))
3845 FB->ReadWritePointer = ptr;
3848 FlushStrBuf(FB->Buf);
3849 FB->ReadWritePointer = NULL;
3852 return eReadSuccess;
3856 * @ingroup StrBuf_CHUNKED_IO
3857 * @brief check whether the chunk-buffer has more data waiting or not.
3858 * @param FB Chunk-Buffer to inspect
3860 eReadState StrBufCheckBuffer(IOBuffer *FB)
3864 if (FB->Buf->BufUsed == 0)
3865 return eReadSuccess;
3866 if (FB->ReadWritePointer == NULL)
3867 return eBufferNotEmpty;
3868 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3869 return eBufferNotEmpty;
3870 return eReadSuccess;
3873 long IOBufferStrLength(IOBuffer *FB)
3875 if ((FB == NULL) || (FB->Buf == NULL))
3877 if (FB->ReadWritePointer == NULL)
3878 return StrLength(FB->Buf);
3880 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3883 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3885 memset(FDB, 0, sizeof(FDIOBuffer));
3887 FDB->TotalSendSize = TotalSendSize;
3889 #ifndef LINUX_SPLICE
3890 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3892 pipe(FDB->SplicePipe);
3897 void FDIOBufferDelete(FDIOBuffer *FDB)
3899 #ifndef LINUX_SPLICE
3900 FreeStrBuf(&FDB->ChunkBuffer);
3902 close(FDB->SplicePipe[0]);
3903 close(FDB->SplicePipe[1]);
3906 close(FDB->OtherFD);
3907 memset(FDB, 0, sizeof(FDIOBuffer));
3910 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3912 ssize_t sent, pipesize;
3914 if (FDB->PipeSize == 0)
3916 pipesize = splice(FDB->OtherFD,
3917 &FDB->TotalSentAlready,
3920 FDB->ChunkSendRemain,
3925 *Err = strerror(errno);
3928 FDB->PipeSize = pipesize;
3930 sent = splice(FDB->SplicePipe[0],
3935 SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
3938 *Err = strerror(errno);
3941 FDB->PipeSize -= sent;
3942 FDB->ChunkSendRemain -= sent;
3949 pRead = FDB->ChunkBuffer->buf;
3950 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3952 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3954 FDB->ChunkBuffer->BufUsed += nRead;
3955 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3957 else if (nRead == 0) {}
3962 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3965 FDB->TotalSentAlready += nRead;
3966 FDB->ChunkSendRemain -= nRead;
3967 return FDB->ChunkSendRemain;
3975 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3977 ssize_t sent, pipesize;
3980 if (FDB->PipeSize == 0)
3982 pipesize = splice(FDB->IOB->fd,
3986 FDB->ChunkSendRemain,
3987 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3991 *Err = strerror(errno);
3994 FDB->PipeSize = pipesize;
3997 sent = splice(FDB->SplicePipe[0],
4000 &FDB->TotalSentAlready,
4002 SPLICE_F_MORE | SPLICE_F_MOVE);
4006 *Err = strerror(errno);
4009 FDB->PipeSize -= sent;
4010 FDB->ChunkSendRemain -= sent;
4014 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
4019 FDB->ChunkBuffer->BufUsed = sent;
4021 while (nWritten < FDB->ChunkBuffer->BufUsed) {
4022 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
4024 *Err = strerror(errno);
4030 FDB->ChunkBuffer->BufUsed = 0;
4031 FDB->TotalSentAlready += sent;
4032 FDB->ChunkSendRemain -= sent;
4033 return FDB->ChunkSendRemain;
4035 else if (sent < 0) {
4036 *Err = strerror(errno);
4044 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
4050 int nSuccessLess = 0;
4054 fdflags = fcntl(FDB->OtherFD, F_GETFL);
4055 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4057 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
4058 (FDB->ChunkSendRemain > 0))
4061 tv.tv_sec = 1; /* selectresolution; */
4065 FD_SET(FDB->OtherFD, &rfds);
4066 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
4067 *Error = strerror(errno);
4071 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
4076 should_write = FDB->IOB->Buf->BufUsed -
4077 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
4078 if (should_write > FDB->ChunkSendRemain)
4079 should_write = FDB->ChunkSendRemain;
4081 rlen = write(FDB->OtherFD,
4082 FDB->IOB->ReadWritePointer,
4085 *Error = strerror(errno);
4089 FDB->TotalSentAlready += rlen;
4090 FDB->IOB->ReadWritePointer += rlen;
4091 FDB->ChunkSendRemain -= rlen;
4093 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
4095 FlushStrBuf(FDB->IOB->Buf);
4096 FDB->IOB->ReadWritePointer = NULL;
4099 if (FDB->ChunkSendRemain == 0)
4100 return eReadSuccess;
4102 return eMustReadMore;
4105 /*******************************************************************************
4106 * File I/O; Prefer buffered read since its faster! *
4107 *******************************************************************************/
4110 * @ingroup StrBuf_IO
4111 * @brief Read a line from socket
4112 * flushes and closes the FD on error
4113 * @param buf the buffer to get the input to
4114 * @param fd pointer to the filedescriptor to read
4115 * @param append Append to an existing string or replace?
4116 * @param Error strerror() on error
4117 * @returns numbers of chars read
4119 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4121 int len, rlen, slen;
4123 if ((buf == NULL) || (buf->buf == NULL)) {
4124 *Error = strerror(EINVAL);
4131 slen = len = buf->BufUsed;
4133 rlen = read(*fd, &buf->buf[len], 1);
4135 *Error = strerror(errno);
4142 if (buf->buf[len] == '\n')
4144 if (buf->buf[len] != '\r')
4146 if (len + 2 >= buf->BufSize) {
4148 buf->buf[len+1] = '\0';
4149 IncreaseBuf(buf, 1, -1);
4153 buf->buf[len] = '\0';
4158 * @ingroup StrBuf_BufferedIO
4159 * @brief Read a line from socket
4160 * flushes and closes the FD on error
4161 * @param Line the line to read from the fd / I/O Buffer
4162 * @param buf the buffer to get the input to
4163 * @param fd pointer to the filedescriptor to read
4164 * @param timeout number of successless selects until we bail out
4165 * @param selectresolution how long to wait on each select
4166 * @param Error strerror() on error
4167 * @returns numbers of chars read
4169 int StrBufTCP_read_buffered_line(StrBuf *Line,
4173 int selectresolution,
4177 int nSuccessLess = 0;
4184 if (buf->BufUsed > 0) {
4185 pch = strchr(buf->buf, '\n');
4188 len = pch - buf->buf;
4189 if (len > 0 && (*(pch - 1) == '\r') )
4191 StrBufSub(Line, buf, 0, len - rlen);
4192 StrBufCutLeft(buf, len + 1);
4197 if (buf->BufSize - buf->BufUsed < 10)
4198 IncreaseBuf(buf, 1, -1);
4200 fdflags = fcntl(*fd, F_GETFL);
4201 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4203 while ((nSuccessLess < timeout) && (pch == NULL)) {
4205 tv.tv_sec = selectresolution;
4210 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4211 *Error = strerror(errno);
4217 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4222 &buf->buf[buf->BufUsed],
4223 buf->BufSize - buf->BufUsed - 1);
4225 *Error = strerror(errno);
4230 else if (rlen > 0) {
4232 buf->BufUsed += rlen;
4233 buf->buf[buf->BufUsed] = '\0';
4234 if (buf->BufUsed + 10 > buf->BufSize) {
4235 IncreaseBuf(buf, 1, -1);
4237 pch = strchr(buf->buf, '\n');
4244 len = pch - buf->buf;
4245 if (len > 0 && (*(pch - 1) == '\r') )
4247 StrBufSub(Line, buf, 0, len - rlen);
4248 StrBufCutLeft(buf, len + 1);
4255 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4256 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4257 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4259 * @ingroup StrBuf_BufferedIO
4260 * @brief Read a line from socket
4261 * flushes and closes the FD on error
4262 * @param Line where to append our Line read from the fd / I/O Buffer;
4263 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4264 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4265 * @param fd pointer to the filedescriptor to read
4266 * @param timeout number of successless selects until we bail out
4267 * @param selectresolution how long to wait on each select
4268 * @param Error strerror() on error
4269 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4271 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4276 int selectresolution,
4279 const char *pche = NULL;
4280 const char *pos = NULL;
4282 int len, rlen, retlen;
4283 int nSuccessLess = 0;
4285 const char *pch = NULL;
4291 if ((Line == NULL) ||
4298 *Error = ErrRBLF_PreConditionFailed;
4303 if ((IOBuf->BufUsed > 0) &&
4305 (pos < IOBuf->buf + IOBuf->BufUsed))
4309 pche = IOBuf->buf + IOBuf->BufUsed;
4313 while ((pch < pche) && (*pch != '\n'))
4315 if (Line->BufUsed + 10 > Line->BufSize)
4318 apos = pcht - Line->buf;
4320 IncreaseBuf(Line, 1, -1);
4321 pcht = Line->buf + apos;
4329 if (len > 0 && (*(pch - 1) == '\r') )
4338 if ((pch >= pche) || (*pch == '\0'))
4346 if ((pch != NULL) &&
4349 if (pch + 1 >= pche) {
4362 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4364 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4365 IncreaseBuf(IOBuf, 1, -1);
4367 fdflags = fcntl(*fd, F_GETFL);
4368 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4371 while ((nSuccessLess < timeout) &&
4381 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4382 *Error = strerror(errno);
4386 *Error = ErrRBLF_SelectFailed;
4389 if (! FD_ISSET(*fd, &rfds) != 0) {
4395 &IOBuf->buf[IOBuf->BufUsed],
4396 IOBuf->BufSize - IOBuf->BufUsed - 1);
4398 *Error = strerror(errno);
4403 else if (rlen > 0) {
4405 pLF = IOBuf->buf + IOBuf->BufUsed;
4406 IOBuf->BufUsed += rlen;
4407 IOBuf->buf[IOBuf->BufUsed] = '\0';
4409 pche = IOBuf->buf + IOBuf->BufUsed;
4411 while ((pLF < pche) && (*pLF != '\n'))
4413 if ((pLF >= pche) || (*pLF == '\0'))
4416 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4420 if (pLF != NULL) apos = pLF - IOBuf->buf;
4421 IncreaseBuf(IOBuf, 1, -1);
4422 if (pLF != NULL) pLF = IOBuf->buf + apos;
4432 if (len > 0 && (*(pLF - 1) == '\r') )
4434 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4435 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4441 return retlen + len;
4443 *Error = ErrRBLF_NotEnoughSentFromServer;
4448 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4450 * @ingroup StrBuf_IO
4451 * @brief Input binary data from socket
4452 * flushes and closes the FD on error
4453 * @param Buf the buffer to get the input to
4454 * @param fd pointer to the filedescriptor to read
4455 * @param append Append to an existing string or replace?
4456 * @param nBytes the maximal number of bytes to read
4457 * @param Error strerror() on error
4458 * @returns numbers of chars read
4460 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4471 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4473 *Error = ErrRBLF_BLOBPreConditionFailed;
4478 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4479 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4481 ptr = Buf->buf + Buf->BufUsed;
4483 fdflags = fcntl(*fd, F_GETFL);
4484 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4486 while ((nRead < nBytes) &&
4496 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4497 *Error = strerror(errno);
4501 *Error = ErrRBLF_SelectFailed;
4504 if (! FD_ISSET(*fd, &rfds) != 0) {
4510 if ((rlen = read(*fd,
4512 nBytes - nRead)) == -1) {
4515 *Error = strerror(errno);
4520 Buf->BufUsed += rlen;
4522 Buf->buf[Buf->BufUsed] = '\0';
4526 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4527 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4529 * @ingroup StrBuf_BufferedIO
4530 * @brief Input binary data from socket
4531 * flushes and closes the FD on error
4532 * @param Blob put binary thing here
4533 * @param IOBuf the buffer to get the input to
4534 * @param Pos offset inside of IOBuf
4535 * @param fd pointer to the filedescriptor to read
4536 * @param append Append to an existing string or replace?
4537 * @param nBytes the maximal number of bytes to read
4538 * @param check whether we should search for '000\n' terminators in case of timeouts
4539 * @param Error strerror() on error
4540 * @returns numbers of chars read
4542 int StrBufReadBLOBBuffered(StrBuf *Blob,
4555 int nAlreadyRead = 0;
4560 int nSuccessLess = 0;
4563 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4567 *Error = ErrRBB_BLOBFPreConditionFailed;
4573 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4574 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4579 rlen = pos - IOBuf->buf;
4580 rlen = IOBuf->BufUsed - rlen;
4583 if ((IOBuf->BufUsed > 0) &&
4585 (pos < IOBuf->buf + IOBuf->BufUsed))
4587 if (rlen < nBytes) {
4588 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4589 Blob->BufUsed += rlen;
4590 Blob->buf[Blob->BufUsed] = '\0';
4591 nAlreadyRead = nRead = rlen;
4594 if (rlen >= nBytes) {
4595 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4596 Blob->BufUsed += nBytes;
4597 Blob->buf[Blob->BufUsed] = '\0';
4598 if (rlen == nBytes) {
4610 if (IOBuf->BufSize < nBytes - nRead)
4611 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4614 fdflags = fcntl(*fd, F_GETFL);
4615 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4623 while ((nSuccessLess < MaxTries) &&
4633 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4634 *Error = strerror(errno);
4638 *Error = ErrRBLF_SelectFailed;
4641 if (! FD_ISSET(*fd, &rfds) != 0) {
4648 IOBuf->BufSize - (ptr - IOBuf->buf));
4652 *Error = strerror(errno);
4655 else if (rlen == 0){
4656 if ((check == NNN_TERM) &&
4658 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4660 StrBufPlain(Blob, HKEY("\n000\n"));
4661 StrBufCutRight(Blob, 5);
4662 return Blob->BufUsed;
4664 else if (!IsNonBlock)
4666 else if (nSuccessLess > MaxTries) {
4668 *Error = ErrRBB_too_many_selects;
4672 else if (rlen > 0) {
4676 IOBuf->BufUsed += rlen;
4679 if (nSuccessLess >= MaxTries) {
4681 *Error = ErrRBB_too_many_selects;
4685 if (nRead > nBytes) {
4686 *Pos = IOBuf->buf + nBytes;
4688 Blob->buf[Blob->BufUsed] = '\0';
4689 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4693 return nRead + nAlreadyRead;
4697 * @ingroup StrBuf_IO
4698 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4699 * @param LineBuf your line will be copied here.
4700 * @param Buf BLOB with lines of text...
4701 * @param Ptr moved arround to keep the next-line across several iterations
4702 * has to be &NULL on start; will be &NotNULL on end of buffer
4703 * @returns size of remaining buffer
4705 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4707 const char *aptr, *ptr, *eptr;
4710 if ((Buf == NULL) ||
4711 (*Ptr == StrBufNOTNULL) ||
4713 (LineBuf->buf == NULL))
4715 *Ptr = StrBufNOTNULL;
4719 FlushStrBuf(LineBuf);
4721 ptr = aptr = Buf->buf;
4725 optr = LineBuf->buf;
4726 eptr = Buf->buf + Buf->BufUsed;
4727 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4729 while ((ptr <= eptr) &&
4736 LineBuf->BufUsed = optr - LineBuf->buf;
4737 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4738 optr = LineBuf->buf + LineBuf->BufUsed;
4739 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4743 if ((ptr >= eptr) && (optr > LineBuf->buf))
4745 LineBuf->BufUsed = optr - LineBuf->buf;
4747 if ((ptr <= eptr) && (*ptr == '\r'))
4749 if ((ptr <= eptr) && (*ptr == '\n'))
4756 *Ptr = StrBufNOTNULL;
4759 return Buf->BufUsed - (ptr - Buf->buf);
4764 * @ingroup StrBuf_IO
4765 * @brief removes double slashes from pathnames
4766 * @param Dir directory string to filter
4767 * @param RemoveTrailingSlash allows / disallows trailing slashes
4769 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4775 while (!IsEmptyStr(a)) {
4787 if ((RemoveTrailingSlash) &&
4793 Dir->BufUsed = b - Dir->buf;