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
32 #ifndef LINUX_SENDFILE
33 #include <sys/sendfile.h>
35 #include "libcitadel.h"
46 #include <sys/sendfile.h>
51 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
52 const Bytef * source, uLong sourceLen, int level);
54 int BaseStrBufSize = 64;
56 const char *StrBufNOTNULL = ((char*) NULL) - 1;
58 const char HexList[256][3] = {
59 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
60 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
61 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
62 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
63 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
64 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
65 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
66 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
67 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
68 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
69 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
70 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
71 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
72 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
73 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
74 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
77 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
78 * StrBuf is a versatile class, aiding the handling of dynamic strings
79 * * reduce de/reallocations
80 * * reduce the need to remeasure it
81 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
82 * * allow asyncroneous IO for line and Blob based operations
83 * * reduce the use of memove in those
84 * * Quick filling in several operations with append functions
88 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
93 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
95 * use these operators to interfere with code demanding char*;
96 * if you need to own the content, smash me. Avoid, since we loose the length information.
100 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
102 * operations to get your Strings into a StrBuf, manipulating them, or appending
105 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
107 * Quick tokenizer; demands of the user to pull its tokens in sequence
111 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
113 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
117 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
119 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
120 * External Cursor to maintain the current read position inside of the buffer
121 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
125 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
131 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
133 * these functions translate the content of a buffer into another representation;
134 * some are combined Fillers and encoders
138 * Private Structure for the Stringbuffer
141 char *buf; /**< the pointer to the dynamic buffer */
142 long BufSize; /**< how many spcae do we optain */
143 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
144 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
146 long nIncreases; /**< for profiling; cound how many times we needed more */
147 char bt [SIZ]; /**< Stacktrace of last increase */
148 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
153 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
154 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
157 #ifdef HAVE_BACKTRACE
158 static void StrBufBacktrace(StrBuf *Buf, int which)
162 void *stack_frames[50];
167 pstart = pch = Buf->bt;
169 pstart = pch = Buf->bt_lastinc;
170 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
171 strings = backtrace_symbols(stack_frames, size);
172 for (i = 0; i < size; i++) {
174 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
176 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
185 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
187 if (hFreeDbglog == -1){
188 pid_t pid = getpid();
190 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
191 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
193 if ((*FreeMe)->nIncreases > 0)
197 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
199 (*FreeMe)->nIncreases,
203 (*FreeMe)->bt_lastinc);
204 n = write(hFreeDbglog, buf, n);
210 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
214 n = write(hFreeDbglog, buf, n);
218 void dbg_IncreaseBuf(StrBuf *IncMe)
221 #ifdef HAVE_BACKTRACE
222 StrBufBacktrace(Buf, 1);
226 void dbg_Init(StrBuf *Buf)
230 Buf->bt_lastinc[0] = '\0';
231 #ifdef HAVE_BACKTRACE
232 StrBufBacktrace(Buf, 0);
238 #define dbg_FreeStrBuf(a, b)
239 #define dbg_IncreaseBuf(a)
246 * @brief swaps the contents of two StrBufs
247 * this is to be used to have cheap switched between a work-buffer and a target buffer
249 * @param B second one
251 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
255 memcpy(&C, A, sizeof(*A));
256 memcpy(A, B, sizeof(*B));
257 memcpy(B, &C, sizeof(C));
262 * @ingroup StrBuf_Cast
263 * @brief Cast operator to Plain String
264 * @note if the buffer is altered by StrBuf operations, this pointer may become
265 * invalid. So don't lean on it after altering the buffer!
266 * Since this operation is considered cheap, rather call it often than risking
267 * your pointer to become invalid!
268 * @param Str the string we want to get the c-string representation for
269 * @returns the Pointer to the Content. Don't mess with it!
271 inline const char *ChrPtr(const StrBuf *Str)
279 * @ingroup StrBuf_Cast
280 * @brief since we know strlen()'s result, provide it here.
281 * @param Str the string to return the length to
282 * @returns contentlength of the buffer
284 inline int StrLength(const StrBuf *Str)
286 return (Str != NULL) ? Str->BufUsed : 0;
290 * @ingroup StrBuf_DeConstructors
291 * @brief local utility function to resize the buffer
292 * @param Buf the buffer whichs storage we should increase
293 * @param KeepOriginal should we copy the original buffer or just start over with a new one
294 * @param DestSize what should fit in after?
296 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
299 size_t NewSize = Buf->BufSize * 2;
305 while ((NewSize <= DestSize) && (NewSize != 0))
311 NewBuf= (char*) malloc(NewSize);
315 if (KeepOriginal && (Buf->BufUsed > 0))
317 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
326 Buf->BufSize = NewSize;
328 dbg_IncreaseBuf(Buf);
334 * @ingroup StrBuf_DeConstructors
335 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
336 * @param Buf Buffer to shrink (has to be empty)
337 * @param ThreshHold if the buffer is bigger then this, its readjusted
338 * @param NewSize if we Shrink it, how big are we going to be afterwards?
340 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
343 (Buf->BufUsed == 0) &&
344 (Buf->BufSize < ThreshHold)) {
346 Buf->buf = (char*) malloc(NewSize);
348 Buf->BufSize = NewSize;
353 * @ingroup StrBuf_DeConstructors
354 * @brief shrink long term buffers to their real size so they don't waste memory
355 * @param Buf buffer to shrink
356 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
357 * @returns physical size of the buffer
359 long StrBufShrinkToFit(StrBuf *Buf, int Force)
364 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
366 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
367 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
368 Buf->BufSize = Buf->BufUsed + 1;
376 * @ingroup StrBuf_DeConstructors
377 * @brief Allocate a new buffer with default buffer size
378 * @returns the new stringbuffer
380 StrBuf* NewStrBuf(void)
384 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
385 NewBuf->buf = (char*) malloc(BaseStrBufSize);
386 NewBuf->buf[0] = '\0';
387 NewBuf->BufSize = BaseStrBufSize;
389 NewBuf->ConstBuf = 0;
397 * @ingroup StrBuf_DeConstructors
398 * @brief Copy Constructor; returns a duplicate of CopyMe
399 * @param CopyMe Buffer to faxmilate
400 * @returns the new stringbuffer
402 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
409 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
410 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
411 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
412 NewBuf->BufUsed = CopyMe->BufUsed;
413 NewBuf->BufSize = CopyMe->BufSize;
414 NewBuf->ConstBuf = 0;
422 * @ingroup StrBuf_DeConstructors
423 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
424 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
425 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
426 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
427 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
428 * @returns the new stringbuffer
430 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
434 if (CreateRelpaceMe == NULL)
439 if (*CreateRelpaceMe != NULL)
440 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
442 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
446 if (CopyFlushMe == NULL)
448 if (*CreateRelpaceMe != NULL)
449 FlushStrBuf(*CreateRelpaceMe);
451 *CreateRelpaceMe = NewStrBuf();
456 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
457 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
458 * be a big IO-Buffer...
460 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
462 if (*CreateRelpaceMe == NULL)
464 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
469 NewBuf = *CreateRelpaceMe;
472 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
476 if (*CreateRelpaceMe == NULL)
478 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
482 NewBuf = *CreateRelpaceMe;
483 SwapBuffers (NewBuf, CopyFlushMe);
486 FlushStrBuf(CopyFlushMe);
491 * @ingroup StrBuf_DeConstructors
492 * @brief create a new Buffer using an existing c-string
493 * this function should also be used if you want to pre-suggest
494 * the buffer size to allocate in conjunction with ptr == NULL
495 * @param ptr the c-string to copy; may be NULL to create a blank instance
496 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
497 * @returns the new stringbuffer
499 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
502 size_t Siz = BaseStrBufSize;
505 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
507 CopySize = strlen((ptr != NULL)?ptr:"");
511 while ((Siz <= CopySize) && (Siz != 0))
519 NewBuf->buf = (char*) malloc(Siz);
520 if (NewBuf->buf == NULL)
525 NewBuf->BufSize = Siz;
527 memcpy(NewBuf->buf, ptr, CopySize);
528 NewBuf->buf[CopySize] = '\0';
529 NewBuf->BufUsed = CopySize;
532 NewBuf->buf[0] = '\0';
535 NewBuf->ConstBuf = 0;
543 * @ingroup StrBuf_DeConstructors
544 * @brief Set an existing buffer from a c-string
545 * @param Buf buffer to load
546 * @param ptr c-string to put into
547 * @param nChars set to -1 if we should work 0-terminated
548 * @returns the new length of the string
550 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
565 CopySize = strlen(ptr);
569 while ((Siz <= CopySize) && (Siz != 0))
577 if (Siz != Buf->BufSize)
578 IncreaseBuf(Buf, 0, Siz);
579 memcpy(Buf->buf, ptr, CopySize);
580 Buf->buf[CopySize] = '\0';
581 Buf->BufUsed = CopySize;
588 * @ingroup StrBuf_DeConstructors
589 * @brief use strbuf as wrapper for a string constant for easy handling
590 * @param StringConstant a string to wrap
591 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
593 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
597 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
598 NewBuf->buf = (char*) StringConstant;
599 NewBuf->BufSize = SizeOfStrConstant;
600 NewBuf->BufUsed = SizeOfStrConstant;
601 NewBuf->ConstBuf = 1;
610 * @ingroup StrBuf_DeConstructors
611 * @brief flush the content of a Buf; keep its struct
612 * @param buf Buffer to flush
614 int FlushStrBuf(StrBuf *buf)
626 * @ingroup StrBuf_DeConstructors
627 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
628 * @param buf Buffer to wipe
630 int FLUSHStrBuf(StrBuf *buf)
636 if (buf->BufUsed > 0) {
637 memset(buf->buf, 0, buf->BufUsed);
644 int hFreeDbglog = -1;
647 * @ingroup StrBuf_DeConstructors
648 * @brief Release a Buffer
649 * Its a double pointer, so it can NULL your pointer
650 * so fancy SIG11 appear instead of random results
651 * @param FreeMe Pointer Pointer to the buffer to free
653 void FreeStrBuf (StrBuf **FreeMe)
658 dbg_FreeStrBuf(FreeMe, 'F');
660 if (!(*FreeMe)->ConstBuf)
661 free((*FreeMe)->buf);
667 * @ingroup StrBuf_DeConstructors
668 * @brief flatten a Buffer to the Char * we return
669 * Its a double pointer, so it can NULL your pointer
670 * so fancy SIG11 appear instead of random results
671 * The Callee then owns the buffer and is responsible for freeing it.
672 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
673 * @returns the pointer of the buffer; Callee owns the memory thereafter.
675 char *SmashStrBuf (StrBuf **SmashMe)
679 if ((SmashMe == NULL) || (*SmashMe == NULL))
682 dbg_FreeStrBuf(SmashMe, 'S');
684 Ret = (*SmashMe)->buf;
691 * @ingroup StrBuf_DeConstructors
692 * @brief Release the buffer
693 * If you want put your StrBuf into a Hash, use this as Destructor.
694 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
696 void HFreeStrBuf (void *VFreeMe)
698 StrBuf *FreeMe = (StrBuf*)VFreeMe;
702 dbg_FreeStrBuf(SmashMe, 'H');
704 if (!FreeMe->ConstBuf)
710 /*******************************************************************************
711 * Simple string transformations *
712 *******************************************************************************/
716 * @brief Wrapper around atol
718 long StrTol(const StrBuf *Buf)
723 return atol(Buf->buf);
730 * @brief Wrapper around atoi
732 int StrToi(const StrBuf *Buf)
736 if (Buf->BufUsed > 0)
737 return atoi(Buf->buf);
744 * @brief Checks to see if the string is a pure number
745 * @param Buf The buffer to inspect
746 * @returns 1 if its a pure number, 0, if not.
748 int StrBufIsNumber(const StrBuf *Buf) {
750 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
753 strtoll(Buf->buf, &pEnd, 10);
754 if (pEnd == Buf->buf)
756 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
758 if (Buf->buf == pEnd)
764 * @ingroup StrBuf_Filler
765 * @brief modifies a Single char of the Buf
766 * You can point to it via char* or a zero-based integer
767 * @param Buf The buffer to manipulate
768 * @param ptr char* to zero; use NULL if unused
769 * @param nThChar zero based pointer into the string; use -1 if unused
770 * @param PeekValue The Character to place into the position
772 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
777 nThChar = ptr - Buf->buf;
778 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
780 Buf->buf[nThChar] = PeekValue;
785 * @ingroup StrBuf_Filler
786 * @brief modifies a range of chars of the Buf
787 * You can point to it via char* or a zero-based integer
788 * @param Buf The buffer to manipulate
789 * @param ptr char* to zero; use NULL if unused
790 * @param nThChar zero based pointer into the string; use -1 if unused
791 * @param nChars how many chars are to be flushed?
792 * @param PookValue The Character to place into that area
794 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
799 nThChar = ptr - Buf->buf;
800 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
802 if (nThChar + nChars > Buf->BufUsed)
803 nChars = Buf->BufUsed - nThChar;
805 memset(Buf->buf + nThChar, PookValue, nChars);
806 /* just to be shure... */
807 Buf->buf[Buf->BufUsed] = 0;
812 * @ingroup StrBuf_Filler
813 * @brief Append a StringBuffer to the buffer
814 * @param Buf Buffer to modify
815 * @param AppendBuf Buffer to copy at the end of our buffer
816 * @param Offset Should we start copying from an offset?
818 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
820 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->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)) return;
1068 if (nChars >= Buf->BufUsed) {
1072 Buf->BufUsed -= nChars;
1073 Buf->buf[Buf->BufUsed] = '\0';
1078 * @brief Cut the string after n Chars
1079 * @param Buf Buffer to modify
1080 * @param AfternChars after how many chars should we trunkate the string?
1081 * @param At if non-null and points inside of our string, cut it there.
1083 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1085 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1087 AfternChars = At - Buf->buf;
1090 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1092 Buf->BufUsed = AfternChars;
1093 Buf->buf[Buf->BufUsed] = '\0';
1099 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1100 * @param Buf the string to modify
1102 void StrBufTrim(StrBuf *Buf)
1105 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1107 while ((Buf->BufUsed > 0) &&
1108 isspace(Buf->buf[Buf->BufUsed - 1]))
1112 Buf->buf[Buf->BufUsed] = '\0';
1114 if (Buf->BufUsed == 0) return;
1116 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1119 if (delta > 0) StrBufCutLeft(Buf, delta);
1123 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1124 * @param Buf the string to modify
1126 void StrBufSpaceToBlank(StrBuf *Buf)
1130 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1133 pche = pch + Buf->BufUsed;
1142 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1150 pLeft = pBuff = Buf->buf;
1151 while (pBuff != NULL) {
1153 pBuff = strchr(pBuff, leftboundary);
1162 pRight = strchr(pBuff, rightboundary);
1164 StrBufCutAt(Buf, 0, pRight);
1166 StrBufCutLeft(Buf, pLeft - Buf->buf);
1171 * @ingroup StrBuf_Filler
1172 * @brief uppercase the contents of a buffer
1173 * @param Buf the buffer to translate
1175 void StrBufUpCase(StrBuf *Buf)
1179 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1182 pche = pch + Buf->BufUsed;
1183 while (pch < pche) {
1184 *pch = toupper(*pch);
1191 * @ingroup StrBuf_Filler
1192 * @brief lowercase the contents of a buffer
1193 * @param Buf the buffer to translate
1195 void StrBufLowerCase(StrBuf *Buf)
1199 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1202 pche = pch + Buf->BufUsed;
1203 while (pch < pche) {
1204 *pch = tolower(*pch);
1210 /*******************************************************************************
1211 * a tokenizer that kills, maims, and destroys *
1212 *******************************************************************************/
1215 * @ingroup StrBuf_Tokenizer
1216 * @brief Replace a token at a given place with a given length by another token with given length
1217 * @param Buf String where to work on
1218 * @param where where inside of the Buf is the search-token
1219 * @param HowLong How long is the token to be replaced
1220 * @param Repl Token to insert at 'where'
1221 * @param ReplLen Length of repl
1222 * @returns -1 if fail else length of resulting Buf
1224 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1225 const char *Repl, long ReplLen)
1228 if ((Buf == NULL) ||
1229 (where > Buf->BufUsed) ||
1230 (where + HowLong > Buf->BufUsed))
1233 if (where + ReplLen - HowLong > Buf->BufSize)
1234 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1237 memmove(Buf->buf + where + ReplLen,
1238 Buf->buf + where + HowLong,
1239 Buf->BufUsed - where - HowLong);
1241 memcpy(Buf->buf + where,
1244 Buf->BufUsed += ReplLen - HowLong;
1246 return Buf->BufUsed;
1250 * @ingroup StrBuf_Tokenizer
1251 * @brief Counts the numbmer of tokens in a buffer
1252 * @param source String to count tokens in
1253 * @param tok Tokenizer char to count
1254 * @returns numbers of tokenizer chars found
1256 int StrBufNum_tokens(const StrBuf *source, char tok)
1260 if ((source == NULL) || (source->BufUsed == 0))
1262 if ((source->BufUsed == 1) && (*source->buf == tok))
1266 pche = pch + source->BufUsed;
1277 * @ingroup StrBuf_Tokenizer
1278 * @brief a string tokenizer
1279 * @param Source StringBuffer to read into
1280 * @param parmnum n'th Parameter to remove
1281 * @param separator tokenizer character
1282 * @returns -1 if not found, else length of token.
1284 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1287 char *d, *s, *end; /* dest, source */
1290 /* Find desired @parameter */
1291 end = Source->buf + Source->BufUsed;
1293 while ((d <= end) &&
1296 /* End of string, bail! */
1301 if (*d == separator) {
1306 if ((d == NULL) || (d >= end))
1307 return 0; /* @Parameter not found */
1309 /* Find next @parameter */
1311 while ((s <= end) &&
1312 (*s && *s != separator))
1316 if (*s == separator)
1320 /* Hack and slash */
1325 memmove(d, s, Source->BufUsed - (s - Source->buf));
1326 Source->BufUsed += ReducedBy;
1327 Source->buf[Source->BufUsed] = '\0';
1329 else if (d == Source->buf) {
1331 Source->BufUsed = 0;
1335 Source->BufUsed += ReducedBy;
1348 * @ingroup StrBuf_Tokenizer
1349 * @brief a string tokenizer
1350 * @param dest Destination StringBuffer
1351 * @param Source StringBuffer to read into
1352 * @param parmnum n'th Parameter to extract
1353 * @param separator tokenizer character
1354 * @returns -1 if not found, else length of token.
1356 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1358 const char *s, *e; //* source * /
1359 int len = 0; //* running total length of extracted string * /
1360 int current_token = 0; //* token currently being processed * /
1363 dest->buf[0] = '\0';
1369 if ((Source == NULL) || (Source->BufUsed ==0)) {
1373 e = s + Source->BufUsed;
1376 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1378 while ((s < e) && !IsEmptyStr(s)) {
1379 if (*s == separator) {
1382 if (len >= dest->BufSize) {
1383 dest->BufUsed = len;
1384 if (IncreaseBuf(dest, 1, -1) < 0) {
1389 if ( (current_token == parmnum) &&
1390 (*s != separator)) {
1391 dest->buf[len] = *s;
1394 else if (current_token > parmnum) {
1400 dest->buf[len] = '\0';
1401 dest->BufUsed = len;
1403 if (current_token < parmnum) {
1404 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1407 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1416 * @ingroup StrBuf_Tokenizer
1417 * @brief a string tokenizer to fetch an integer
1418 * @param Source String containing tokens
1419 * @param parmnum n'th Parameter to extract
1420 * @param separator tokenizer character
1421 * @returns 0 if not found, else integer representation of the token
1423 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1433 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1440 * @ingroup StrBuf_Tokenizer
1441 * @brief a string tokenizer to fetch a long integer
1442 * @param Source String containing tokens
1443 * @param parmnum n'th Parameter to extract
1444 * @param separator tokenizer character
1445 * @returns 0 if not found, else long integer representation of the token
1447 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1457 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1465 * @ingroup StrBuf_Tokenizer
1466 * @brief a string tokenizer to fetch an unsigned long
1467 * @param Source String containing tokens
1468 * @param parmnum n'th Parameter to extract
1469 * @param separator tokenizer character
1470 * @returns 0 if not found, else unsigned long representation of the token
1472 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1483 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1487 return (unsigned long) atol(pnum);
1496 * @ingroup StrBuf_NextTokenizer
1497 * @brief a string tokenizer; Bounds checker
1498 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1499 * @param Source our tokenbuffer
1500 * @param pStart the token iterator pointer to inspect
1501 * @returns whether the revolving pointer is inside of the search range
1503 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1505 if ((Source == NULL) ||
1506 (*pStart == StrBufNOTNULL) ||
1507 (Source->BufUsed == 0))
1511 if (*pStart == NULL)
1515 else if (*pStart > Source->buf + Source->BufUsed)
1519 else if (*pStart <= Source->buf)
1528 * @ingroup StrBuf_NextTokenizer
1529 * @brief a string tokenizer
1530 * @param dest Destination StringBuffer
1531 * @param Source StringBuffer to read into
1532 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1533 * @param separator tokenizer
1534 * @returns -1 if not found, else length of token.
1536 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1538 const char *s; /* source */
1539 const char *EndBuffer; /* end stop of source buffer */
1540 int current_token = 0; /* token currently being processed */
1541 int len = 0; /* running total length of extracted string */
1543 if ((Source == NULL) ||
1544 (Source->BufUsed == 0) )
1546 *pStart = StrBufNOTNULL;
1552 EndBuffer = Source->buf + Source->BufUsed;
1556 dest->buf[0] = '\0';
1561 *pStart = EndBuffer + 1;
1565 if (*pStart == NULL)
1567 *pStart = Source->buf; /* we're starting to examine this buffer. */
1569 else if ((*pStart < Source->buf) ||
1570 (*pStart > EndBuffer ) )
1572 return -1; /* no more tokens to find. */
1576 /* start to find the next token */
1577 while ((s <= EndBuffer) &&
1578 (current_token == 0) )
1580 if (*s == separator)
1582 /* we found the next token */
1586 if (len >= dest->BufSize)
1588 /* our Dest-buffer isn't big enough, increase it. */
1589 dest->BufUsed = len;
1591 if (IncreaseBuf(dest, 1, -1) < 0) {
1592 /* WHUT? no more mem? bail out. */
1599 if ( (current_token == 0 ) && /* are we in our target token? */
1600 (!IsEmptyStr(s) ) &&
1601 (separator != *s) ) /* don't copy the token itself */
1603 dest->buf[len] = *s; /* Copy the payload */
1604 ++len; /* remember the bigger size. */
1610 /* did we reach the end? */
1611 if ((s > EndBuffer)) {
1612 EndBuffer = StrBufNOTNULL;
1613 *pStart = EndBuffer;
1616 *pStart = s; /* remember the position for the next run */
1619 /* sanitize our extracted token */
1620 dest->buf[len] = '\0';
1621 dest->BufUsed = len;
1628 * @ingroup StrBuf_NextTokenizer
1629 * @brief a string tokenizer
1630 * @param Source StringBuffer to read from
1631 * @param pStart pointer to the end of the last token. Feed with NULL.
1632 * @param separator tokenizer character
1633 * @param nTokens number of tokens to fastforward over
1634 * @returns -1 if not found, else length of token.
1636 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1638 const char *s, *EndBuffer; //* source * /
1639 int len = 0; //* running total length of extracted string * /
1640 int current_token = 0; //* token currently being processed * /
1642 if ((Source == NULL) ||
1643 (Source->BufUsed ==0)) {
1647 return Source->BufUsed;
1649 if (*pStart == NULL)
1650 *pStart = Source->buf;
1652 EndBuffer = Source->buf + Source->BufUsed;
1654 if ((*pStart < Source->buf) ||
1655 (*pStart > EndBuffer)) {
1663 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1665 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1666 if (*s == separator) {
1669 if (current_token >= nTokens) {
1681 * @ingroup StrBuf_NextTokenizer
1682 * @brief a string tokenizer to fetch an integer
1683 * @param Source StringBuffer to read from
1684 * @param pStart Cursor on the tokenstring
1685 * @param separator tokenizer character
1686 * @returns 0 if not found, else integer representation of the token
1688 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1698 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1705 * @ingroup StrBuf_NextTokenizer
1706 * @brief a string tokenizer to fetch a long integer
1707 * @param Source StringBuffer to read from
1708 * @param pStart Cursor on the tokenstring
1709 * @param separator tokenizer character
1710 * @returns 0 if not found, else long integer representation of the token
1712 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1722 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1730 * @ingroup StrBuf_NextTokenizer
1731 * @brief a string tokenizer to fetch an unsigned long
1732 * @param Source StringBuffer to read from
1733 * @param pStart Cursor on the tokenstring
1734 * @param separator tokenizer character
1735 * @returns 0 if not found, else unsigned long representation of the token
1737 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1748 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1752 return (unsigned long) atol(pnum);
1762 /*******************************************************************************
1763 * Escape Appending *
1764 *******************************************************************************/
1767 * @ingroup StrBuf_DeEnCoder
1768 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1769 * @param OutBuf the output buffer
1770 * @param In Buffer to encode
1771 * @param PlainIn way in from plain old c strings
1773 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1775 const char *pch, *pche;
1779 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1781 if (PlainIn != NULL) {
1782 len = strlen(PlainIn);
1788 pche = pch + In->BufUsed;
1795 pt = OutBuf->buf + OutBuf->BufUsed;
1796 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1798 while (pch < pche) {
1800 IncreaseBuf(OutBuf, 1, -1);
1801 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1802 pt = OutBuf->buf + OutBuf->BufUsed;
1805 if((*pch >= 'a' && *pch <= 'z') ||
1806 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1807 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1808 (*pch == '!') || (*pch == '_') ||
1809 (*pch == ',') || (*pch == '.'))
1816 *(pt + 1) = HexList[(unsigned char)*pch][0];
1817 *(pt + 2) = HexList[(unsigned char)*pch][1];
1819 OutBuf->BufUsed += 3;
1827 * @ingroup StrBuf_DeEnCoder
1828 * @brief append a string in hex encoding to the buffer
1829 * @param OutBuf the output buffer
1830 * @param In Buffer to encode
1831 * @param PlainIn way in from plain old c strings
1832 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1834 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1836 const unsigned char *pch, *pche;
1840 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1842 if (PlainIn != NULL) {
1844 len = strlen((const char*)PlainIn);
1851 pch = (const unsigned char*)In->buf;
1852 pche = pch + In->BufUsed;
1859 pt = OutBuf->buf + OutBuf->BufUsed;
1860 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1862 while (pch < pche) {
1864 IncreaseBuf(OutBuf, 1, -1);
1865 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1866 pt = OutBuf->buf + OutBuf->BufUsed;
1869 *pt = HexList[*pch][0];
1871 *pt = HexList[*pch][1];
1872 pt ++; pch ++; OutBuf->BufUsed += 2;
1878 * @ingroup StrBuf_DeEnCoder
1879 * @brief append a string in hex encoding to the buffer
1880 * @param OutBuf the output buffer
1881 * @param In Buffer to encode
1882 * @param PlainIn way in from plain old c strings
1884 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1886 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1890 * @ingroup StrBuf_DeEnCoder
1891 * @brief Append a string, escaping characters which have meaning in HTML.
1893 * @param Target target buffer
1894 * @param Source source buffer; set to NULL if you just have a C-String
1895 * @param PlainIn Plain-C string to append; set to NULL if unused
1896 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1897 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1898 * if set to 2, linebreaks are replaced by <br/>
1900 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1902 const char *aptr, *eiptr;
1906 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1909 if (PlainIn != NULL) {
1911 len = strlen(PlainIn);
1916 eiptr = aptr + Source->BufUsed;
1917 len = Source->BufUsed;
1923 bptr = Target->buf + Target->BufUsed;
1924 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1926 while (aptr < eiptr){
1928 IncreaseBuf(Target, 1, -1);
1929 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1930 bptr = Target->buf + Target->BufUsed;
1933 memcpy(bptr, "<", 4);
1935 Target->BufUsed += 4;
1937 else if (*aptr == '>') {
1938 memcpy(bptr, ">", 4);
1940 Target->BufUsed += 4;
1942 else if (*aptr == '&') {
1943 memcpy(bptr, "&", 5);
1945 Target->BufUsed += 5;
1947 else if (*aptr == '"') {
1948 memcpy(bptr, """, 6);
1950 Target->BufUsed += 6;
1952 else if (*aptr == '\'') {
1953 memcpy(bptr, "'", 5);
1955 Target->BufUsed += 5;
1957 else if (*aptr == LB) {
1962 else if (*aptr == RB) {
1967 else if (*aptr == QU) {
1972 else if ((*aptr == 32) && (nbsp == 1)) {
1973 memcpy(bptr, " ", 6);
1975 Target->BufUsed += 6;
1977 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1978 *bptr='\0'; /* nothing */
1980 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1981 memcpy(bptr, "<br/>", 11);
1983 Target->BufUsed += 11;
1987 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1988 *bptr='\0'; /* nothing */
1998 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2000 return Target->BufUsed;
2004 * @ingroup StrBuf_DeEnCoder
2005 * @brief Append a string, escaping characters which have meaning in HTML.
2006 * Converts linebreaks into blanks; escapes single quotes
2007 * @param Target target buffer
2008 * @param Source source buffer; set to NULL if you just have a C-String
2009 * @param PlainIn Plain-C string to append; set to NULL if unused
2011 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2013 const char *aptr, *eiptr;
2017 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2020 if (PlainIn != NULL) {
2022 len = strlen(PlainIn);
2027 eiptr = aptr + Source->BufUsed;
2028 len = Source->BufUsed;
2034 eptr = Target->buf + Target->BufSize - 8;
2035 tptr = Target->buf + Target->BufUsed;
2037 while (aptr < eiptr){
2039 IncreaseBuf(Target, 1, -1);
2040 eptr = Target->buf + Target->BufSize - 8;
2041 tptr = Target->buf + Target->BufUsed;
2044 if (*aptr == '\n') {
2048 else if (*aptr == '\r') {
2052 else if (*aptr == '\'') {
2058 Target->BufUsed += 5;
2071 * @ingroup StrBuf_DeEnCoder
2072 * @brief Append a string, escaping characters which have meaning in ICAL.
2074 * @param Target target buffer
2075 * @param Source source buffer; set to NULL if you just have a C-String
2076 * @param PlainIn Plain-C string to append; set to NULL if unused
2078 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2080 const char *aptr, *eiptr;
2084 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2087 if (PlainIn != NULL) {
2089 len = strlen(PlainIn);
2094 eiptr = aptr + Source->BufUsed;
2095 len = Source->BufUsed;
2101 eptr = Target->buf + Target->BufSize - 8;
2102 tptr = Target->buf + Target->BufUsed;
2104 while (aptr < eiptr){
2105 if(tptr + 3 >= eptr) {
2106 IncreaseBuf(Target, 1, -1);
2107 eptr = Target->buf + Target->BufSize - 8;
2108 tptr = Target->buf + Target->BufUsed;
2111 if (*aptr == '\n') {
2118 else if (*aptr == '\r') {
2125 else if (*aptr == ',') {
2141 * @ingroup StrBuf_DeEnCoder
2142 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2144 * @param Target target buffer
2145 * @param Source source buffer; set to NULL if you just have a C-String
2146 * @param PlainIn Plain-C string to append; set to NULL if unused
2147 * @returns size of result or -1
2149 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2151 const char *aptr, *eiptr;
2156 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2159 if (PlainIn != NULL) {
2161 len = strlen(PlainIn);
2166 eiptr = aptr + Source->BufUsed;
2167 len = Source->BufUsed;
2173 bptr = Target->buf + Target->BufUsed;
2174 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2176 while (aptr < eiptr){
2178 IncreaseBuf(Target, 1, -1);
2179 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2180 bptr = Target->buf + Target->BufUsed;
2184 memcpy(bptr, HKEY("\\n"));
2186 Target->BufUsed += 2;
2189 memcpy(bptr, HKEY("\\r"));
2191 Target->BufUsed += 2;
2198 Target->BufUsed += 2;
2201 if ((*(aptr + 1) == 'u') &&
2202 isxdigit(*(aptr + 2)) &&
2203 isxdigit(*(aptr + 3)) &&
2204 isxdigit(*(aptr + 4)) &&
2205 isxdigit(*(aptr + 5)))
2206 { /* oh, a unicode escaper. let it pass through. */
2207 memcpy(bptr, aptr, 6);
2210 Target->BufUsed += 6;
2218 Target->BufUsed += 2;
2226 Target->BufUsed += 2;
2233 Target->BufUsed += 2;
2240 Target->BufUsed += 2;
2243 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2244 while (IsUtf8Sequence > 0){
2247 if (--IsUtf8Sequence)
2255 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2257 return Target->BufUsed;
2261 * @ingroup StrBuf_DeEnCoder
2262 * @brief Append a string, escaping characters which have meaning in HTML + json.
2264 * @param Target target buffer
2265 * @param Source source buffer; set to NULL if you just have a C-String
2266 * @param PlainIn Plain-C string to append; set to NULL if unused
2267 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2268 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2269 * if set to 2, linebreaks are replaced by <br/>
2271 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2273 const char *aptr, *eiptr;
2276 int IsUtf8Sequence = 0;
2278 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2281 if (PlainIn != NULL) {
2283 len = strlen(PlainIn);
2288 eiptr = aptr + Source->BufUsed;
2289 len = Source->BufUsed;
2295 bptr = Target->buf + Target->BufUsed;
2296 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2298 while (aptr < eiptr){
2300 IncreaseBuf(Target, 1, -1);
2301 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2302 bptr = Target->buf + Target->BufUsed;
2306 memcpy(bptr, HKEY("<"));
2308 Target->BufUsed += 4;
2311 memcpy(bptr, HKEY(">"));
2313 Target->BufUsed += 4;
2316 memcpy(bptr, HKEY("&"));
2318 Target->BufUsed += 5;
2331 switch (nolinebreaks) {
2333 *bptr='\0'; /* nothing */
2336 memcpy(bptr, HKEY("<br/>"));
2338 Target->BufUsed += 11;
2341 memcpy(bptr, HKEY("\\n"));
2343 Target->BufUsed += 2;
2347 switch (nolinebreaks) {
2350 *bptr='\0'; /* nothing */
2353 memcpy(bptr, HKEY("\\r"));
2355 Target->BufUsed += 2;
2365 Target->BufUsed += 2;
2368 if ((*(aptr + 1) == 'u') &&
2369 isxdigit(*(aptr + 2)) &&
2370 isxdigit(*(aptr + 3)) &&
2371 isxdigit(*(aptr + 4)) &&
2372 isxdigit(*(aptr + 5)))
2373 { /* oh, a unicode escaper. let it pass through. */
2374 memcpy(bptr, aptr, 6);
2377 Target->BufUsed += 6;
2385 Target->BufUsed += 2;
2393 Target->BufUsed += 2;
2400 Target->BufUsed += 2;
2407 Target->BufUsed += 2;
2411 memcpy(bptr, HKEY(" "));
2413 Target->BufUsed += 6;
2417 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2418 while (IsUtf8Sequence > 0){
2421 if (--IsUtf8Sequence)
2429 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2431 return Target->BufUsed;
2435 * @ingroup StrBuf_DeEnCoder
2436 * @brief unhide special chars hidden to the HTML escaper
2437 * @param target buffer to put the unescaped string in
2438 * @param source buffer to unescape
2440 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2446 FlushStrBuf(target);
2448 if (source == NULL ||target == NULL)
2453 len = source->BufUsed;
2454 for (a = 0; a < len; ++a) {
2455 if (target->BufUsed >= target->BufSize)
2456 IncreaseBuf(target, 1, -1);
2458 if (source->buf[a] == '=') {
2459 hex[0] = source->buf[a + 1];
2460 hex[1] = source->buf[a + 2];
2463 sscanf(hex, "%02x", &b);
2464 target->buf[target->BufUsed] = b;
2465 target->buf[++target->BufUsed] = 0;
2469 target->buf[target->BufUsed] = source->buf[a];
2470 target->buf[++target->BufUsed] = 0;
2477 * @ingroup StrBuf_DeEnCoder
2478 * @brief hide special chars from the HTML escapers and friends
2479 * @param target buffer to put the escaped string in
2480 * @param source buffer to escape
2482 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2487 FlushStrBuf(target);
2489 if (source == NULL ||target == NULL)
2494 len = source->BufUsed;
2495 for (i=0; i<len; ++i) {
2496 if (target->BufUsed + 4 >= target->BufSize)
2497 IncreaseBuf(target, 1, -1);
2498 if ( (isalnum(source->buf[i])) ||
2499 (source->buf[i]=='-') ||
2500 (source->buf[i]=='_') ) {
2501 target->buf[target->BufUsed++] = source->buf[i];
2504 sprintf(&target->buf[target->BufUsed],
2506 (0xFF &source->buf[i]));
2507 target->BufUsed += 3;
2510 target->buf[target->BufUsed + 1] = '\0';
2514 /*******************************************************************************
2515 * Quoted Printable de/encoding *
2516 *******************************************************************************/
2519 * @ingroup StrBuf_DeEnCoder
2520 * @brief decode a buffer from base 64 encoding; destroys original
2521 * @param Buf Buffor to transform
2523 int StrBufDecodeBase64(StrBuf *Buf)
2527 if (Buf == NULL) return -1;
2529 xferbuf = (char*) malloc(Buf->BufSize);
2531 siz = CtdlDecodeBase64(xferbuf,
2541 * @ingroup StrBuf_DeEnCoder
2542 * @brief decode a buffer from base 64 encoding; destroys original
2543 * @param Buf Buffor to transform
2545 int StrBufDecodeHex(StrBuf *Buf)
2548 char *pch, *pche, *pchi;
2550 if (Buf == NULL) return -1;
2552 pch = pchi = Buf->buf;
2553 pche = pch + Buf->BufUsed;
2555 while (pchi < pche){
2556 ch = decode_hex(pchi);
2563 Buf->BufUsed = pch - Buf->buf;
2564 return Buf->BufUsed;
2568 * @ingroup StrBuf_DeEnCoder
2569 * @brief replace all chars >0x20 && < 0x7F with Mute
2570 * @param Mute char to put over invalid chars
2571 * @param Buf Buffor to transform
2573 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2577 if (Buf == NULL) return -1;
2578 pch = (unsigned char *)Buf->buf;
2579 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2580 if ((*pch < 0x20) || (*pch > 0x7F))
2584 return Buf->BufUsed;
2589 * @ingroup StrBuf_DeEnCoder
2590 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2591 * @param Buf Buffer to translate
2592 * @param StripBlanks Reduce several blanks to one?
2594 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2603 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2604 Buf->buf[Buf->BufUsed - 1] = '\0';
2609 while (a < Buf->BufUsed) {
2610 if (Buf->buf[a] == '+')
2612 else if (Buf->buf[a] == '%') {
2613 /* don't let % chars through, rather truncate the input. */
2614 if (a + 2 > Buf->BufUsed) {
2619 hex[0] = Buf->buf[a + 1];
2620 hex[1] = Buf->buf[a + 2];
2623 sscanf(hex, "%02x", &b);
2624 Buf->buf[a] = (char) b;
2625 len = Buf->BufUsed - a - 2;
2627 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2639 * @ingroup StrBuf_DeEnCoder
2640 * @brief RFC2047-encode a header field if necessary.
2641 * If no non-ASCII characters are found, the string
2642 * will be copied verbatim without encoding.
2644 * @param target Target buffer.
2645 * @param source Source string to be encoded.
2646 * @returns encoded length; -1 if non success.
2648 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2650 const char headerStr[] = "=?UTF-8?Q?";
2651 int need_to_encode = 0;
2655 if ((source == NULL) ||
2659 while ((i < source->BufUsed) &&
2660 (!IsEmptyStr (&source->buf[i])) &&
2661 (need_to_encode == 0)) {
2662 if (((unsigned char) source->buf[i] < 32) ||
2663 ((unsigned char) source->buf[i] > 126)) {
2669 if (!need_to_encode) {
2670 if (*target == NULL) {
2671 *target = NewStrBufPlain(source->buf, source->BufUsed);
2674 FlushStrBuf(*target);
2675 StrBufAppendBuf(*target, source, 0);
2677 return (*target)->BufUsed;
2679 if (*target == NULL)
2680 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2681 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2682 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2683 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2684 (*target)->BufUsed = sizeof(headerStr) - 1;
2685 for (i=0; (i < source->BufUsed); ++i) {
2686 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2687 IncreaseBuf(*target, 1, 0);
2688 ch = (unsigned char) source->buf[i];
2698 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2699 (*target)->BufUsed += 3;
2703 (*target)->buf[(*target)->BufUsed] = '_';
2705 (*target)->buf[(*target)->BufUsed] = ch;
2706 (*target)->BufUsed++;
2710 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2711 IncreaseBuf(*target, 1, 0);
2713 (*target)->buf[(*target)->BufUsed++] = '?';
2714 (*target)->buf[(*target)->BufUsed++] = '=';
2715 (*target)->buf[(*target)->BufUsed] = '\0';
2716 return (*target)->BufUsed;;
2721 static void AddRecipient(StrBuf *Target,
2723 StrBuf *EmailAddress,
2728 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2729 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2731 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2732 StrBufRFC2047encode(&EncBuf, UserName);
2733 StrBufAppendBuf(Target, EncBuf, 0);
2734 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2735 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2737 if (StrLength(EmailAddress) > 0){
2738 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2739 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2740 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2746 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2747 * \param Recp Source list of email recipients
2748 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2749 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2750 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2751 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2753 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2755 StrBuf *EmailAddress,
2759 const char *pch, *pche;
2760 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2762 if ((Recp == NULL) || (StrLength(Recp) == 0))
2766 pche = pch + StrLength(Recp);
2768 if (!CheckEncode(pch, -1, pche))
2769 return NewStrBufDup(Recp);
2771 Target = NewStrBufPlain(NULL, StrLength(Recp));
2773 while ((pch != NULL) && (pch < pche))
2775 while (isspace(*pch)) pch++;
2776 UserStart = UserEnd = EmailStart = EmailEnd = NULL;
2778 if ((*pch == '"') || (*pch == '\'')) {
2779 UserStart = pch + 1;
2781 UserEnd = strchr(UserStart, *pch);
2782 if (UserEnd == NULL)
2783 break; ///TODO: Userfeedback??
2784 EmailStart = UserEnd + 1;
2785 while (isspace(*EmailStart))
2787 if (UserEnd == UserStart) {
2788 UserStart = UserEnd = NULL;
2791 if (*EmailStart == '<') {
2793 EmailEnd = strchr(EmailStart, '>');
2794 if (EmailEnd == NULL)
2795 EmailEnd = strchr(EmailStart, ',');
2799 EmailEnd = strchr(EmailStart, ',');
2801 if (EmailEnd == NULL)
2808 EmailEnd = strchr(UserStart, ',');
2809 if (EmailEnd == NULL) {
2810 EmailEnd = strchr(pch, '>');
2812 if (EmailEnd != NULL) {
2822 while ((EmailEnd > UserStart) && !gt &&
2823 ((*EmailEnd == ',') ||
2824 (*EmailEnd == '>') ||
2825 (isspace(*EmailEnd))))
2827 if (*EmailEnd == '>')
2832 if (EmailEnd == UserStart)
2836 EmailStart = strchr(UserStart, '<');
2837 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2839 UserEnd = EmailStart;
2841 while ((UserEnd > UserStart) &&
2842 isspace (*(UserEnd - 1)))
2845 if (UserStart >= UserEnd)
2846 UserStart = UserEnd = NULL;
2847 At = strchr(EmailStart, '@');
2849 else { /* this is a local recipient... no domain, just a realname */
2850 EmailStart = UserStart;
2851 At = strchr(EmailStart, '@');
2857 EmailStart = UserStart;
2863 if ((UserStart != NULL) && (UserEnd != NULL))
2864 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2865 else if ((UserStart != NULL) && (UserEnd == NULL))
2866 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2868 FlushStrBuf(UserName);
2870 if ((EmailStart != NULL) && (EmailEnd != NULL))
2871 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2872 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2873 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2875 FlushStrBuf(EmailAddress);
2877 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2882 if ((pch != NULL) && (*pch == ','))
2884 if (pch != NULL) while (isspace(*pch))
2893 * @brief replaces all occurances of 'search' by 'replace'
2894 * @param buf Buffer to modify
2895 * @param search character to search
2896 * @param replace character to replace search by
2898 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2903 for (i=0; i<buf->BufUsed; i++)
2904 if (buf->buf[i] == search)
2905 buf->buf[i] = replace;
2911 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2912 * @param buf Buffer to modify
2914 void StrBufToUnixLF(StrBuf *buf)
2916 char *pche, *pchS, *pchT;
2920 pche = buf->buf + buf->BufUsed;
2921 pchS = pchT = buf->buf;
2927 if (*pchS != '\n') {
2936 buf->BufUsed = pchT - buf->buf;
2940 /*******************************************************************************
2941 * Iconv Wrapper; RFC822 de/encoding *
2942 *******************************************************************************/
2945 * @ingroup StrBuf_DeEnCoder
2946 * @brief Wrapper around iconv_open()
2947 * Our version adds aliases for non-standard Microsoft charsets
2948 * such as 'MS950', aliasing them to names like 'CP950'
2950 * @param tocode Target encoding
2951 * @param fromcode Source encoding
2952 * @param pic anonimized pointer to iconv struct
2954 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2957 iconv_t ic = (iconv_t)(-1) ;
2958 ic = iconv_open(tocode, fromcode);
2959 if (ic == (iconv_t)(-1) ) {
2960 char alias_fromcode[64];
2961 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2962 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2963 alias_fromcode[0] = 'C';
2964 alias_fromcode[1] = 'P';
2965 ic = iconv_open(tocode, alias_fromcode);
2968 *(iconv_t *)pic = ic;
2974 * @ingroup StrBuf_DeEnCoder
2975 * @brief find one chunk of a RFC822 encoded string
2976 * @param Buffer where to search
2977 * @param bptr where to start searching
2978 * @returns found position, NULL if none.
2980 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
2983 /* Find the next ?Q? */
2984 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
2987 end = strchr(bptr + 2, '?');
2992 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2993 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
2994 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
2995 (*(end + 2) == '?')) {
2996 /* skip on to the end of the cluster, the next ?= */
2997 end = strstr(end + 3, "?=");
3000 /* sort of half valid encoding, try to find an end. */
3001 end = strstr(bptr, "?=");
3008 * @ingroup StrBuf_DeEnCoder
3009 * @brief convert one buffer according to the preselected iconv pointer PIC
3010 * @param ConvertBuf buffer we need to translate
3011 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3012 * @param pic Pointer to the iconv-session Object
3014 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3020 char *ibuf; /**< Buffer of characters to be converted */
3021 char *obuf; /**< Buffer for converted characters */
3022 size_t ibuflen; /**< Length of input buffer */
3023 size_t obuflen; /**< Length of output buffer */
3026 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3027 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3028 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3030 ic = *(iconv_t*)pic;
3031 ibuf = ConvertBuf->buf;
3032 ibuflen = ConvertBuf->BufUsed;
3034 obuflen = TmpBuf->BufSize;
3036 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3039 if (errno == E2BIG) {
3041 IncreaseBuf(TmpBuf, 0, 0);
3046 else if (errno == EILSEQ){
3047 /* hm, invalid utf8 sequence... what to do now? */
3048 /* An invalid multibyte sequence has been encountered in the input */
3050 else if (errno == EINVAL) {
3051 /* An incomplete multibyte sequence has been encountered in the input. */
3054 FlushStrBuf(TmpBuf);
3057 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3058 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3060 /* little card game: wheres the red lady? */
3061 SwapBuffers(ConvertBuf, TmpBuf);
3062 FlushStrBuf(TmpBuf);
3069 * @ingroup StrBuf_DeEnCoder
3070 * @brief catches one RFC822 encoded segment, and decodes it.
3071 * @param Target buffer to fill with result
3072 * @param DecodeMe buffer with stuff to process
3073 * @param SegmentStart points to our current segment in DecodeMe
3074 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3075 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3076 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3077 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3079 inline static void DecodeSegment(StrBuf *Target,
3080 const StrBuf *DecodeMe,
3081 const char *SegmentStart,
3082 const char *SegmentEnd,
3084 StrBuf *ConvertBuf2,
3085 StrBuf *FoundCharset)
3091 iconv_t ic = (iconv_t)(-1);
3095 /* Now we handle foreign character sets properly encoded
3096 * in RFC2047 format.
3098 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3099 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3100 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3101 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3102 if (FoundCharset != NULL) {
3103 FlushStrBuf(FoundCharset);
3104 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3106 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3107 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3109 *encoding = toupper(*encoding);
3110 if (*encoding == 'B') { /**< base64 */
3111 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3112 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3113 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3115 ConvertBuf->BufUsed);
3117 else if (*encoding == 'Q') { /**< quoted-printable */
3121 while (pos < ConvertBuf->BufUsed)
3123 if (ConvertBuf->buf[pos] == '_')
3124 ConvertBuf->buf[pos] = ' ';
3128 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3129 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3131 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3134 ConvertBuf->BufUsed);
3137 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3140 ctdl_iconv_open("UTF-8", charset, &ic);
3141 if (ic != (iconv_t)(-1) ) {
3143 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3144 StrBufAppendBuf(Target, ConvertBuf2, 0);
3149 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3155 * @ingroup StrBuf_DeEnCoder
3156 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3157 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3158 * @param Target where to put the decoded string to
3159 * @param DecodeMe buffer with encoded string
3160 * @param DefaultCharset if we don't find one, which should we use?
3161 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3162 * put it here for later use where no string might be known.
3164 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3167 StrBuf *ConvertBuf2;
3168 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3169 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3171 StrBuf_RFC822_2_Utf8(Target,
3177 FreeStrBuf(&ConvertBuf);
3178 FreeStrBuf(&ConvertBuf2);
3182 * @ingroup StrBuf_DeEnCoder
3183 * @brief Handle subjects with RFC2047 encoding such as:
3184 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3185 * @param Target where to put the decoded string to
3186 * @param DecodeMe buffer with encoded string
3187 * @param DefaultCharset if we don't find one, which should we use?
3188 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3189 * put it here for later use where no string might be known.
3190 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3191 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3193 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3194 const StrBuf *DecodeMe,
3195 const StrBuf* DefaultCharset,
3196 StrBuf *FoundCharset,
3198 StrBuf *ConvertBuf2)
3200 StrBuf *DecodedInvalidBuf = NULL;
3201 const StrBuf *DecodeMee = DecodeMe;
3202 const char *start, *end, *next, *nextend, *ptr = NULL;
3204 iconv_t ic = (iconv_t)(-1) ;
3209 int illegal_non_rfc2047_encoding = 0;
3211 /* Sometimes, badly formed messages contain strings which were simply
3212 * written out directly in some foreign character set instead of
3213 * using RFC2047 encoding. This is illegal but we will attempt to
3214 * handle it anyway by converting from a user-specified default
3215 * charset to UTF-8 if we see any nonprintable characters.
3218 len = StrLength(DecodeMe);
3219 for (i=0; i<DecodeMe->BufUsed; ++i) {
3220 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3221 illegal_non_rfc2047_encoding = 1;
3226 if ((illegal_non_rfc2047_encoding) &&
3227 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3228 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3231 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3232 if (ic != (iconv_t)(-1) ) {
3233 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3234 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3235 DecodeMee = DecodedInvalidBuf;
3241 /* pre evaluate the first pair */
3242 nextend = end = NULL;
3243 len = StrLength(DecodeMee);
3244 start = strstr(DecodeMee->buf, "=?");
3245 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3247 end = FindNextEnd (DecodeMee, start + 2);
3249 StrBufAppendBuf(Target, DecodeMee, 0);
3250 FreeStrBuf(&DecodedInvalidBuf);
3255 if (start != DecodeMee->buf) {
3258 nFront = start - DecodeMee->buf;
3259 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3263 * Since spammers will go to all sorts of absurd lengths to get their
3264 * messages through, there are LOTS of corrupt headers out there.
3265 * So, prevent a really badly formed RFC2047 header from throwing
3266 * this function into an infinite loop.
3268 while ((start != NULL) &&
3275 DecodeSegment(Target,
3283 next = strstr(end, "=?");
3285 if ((next != NULL) &&
3287 nextend = FindNextEnd(DecodeMee, next);
3288 if (nextend == NULL)
3291 /* did we find two partitions */
3292 if ((next != NULL) &&
3296 while ((ptr < next) &&
3303 * did we find a gab just filled with blanks?
3304 * if not, copy its stuff over.
3308 StrBufAppendBufPlain(Target,
3314 /* our next-pair is our new first pair now. */
3320 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3321 if ((end != NULL) && (end < nextend)) {
3323 while ( (ptr < nextend) &&
3330 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3332 FreeStrBuf(&DecodedInvalidBuf);
3335 /*******************************************************************************
3336 * Manipulating UTF-8 Strings *
3337 *******************************************************************************/
3341 * @brief evaluate the length of an utf8 special character sequence
3342 * @param Char the character to examine
3343 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3345 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3348 unsigned char test = (1<<7);
3350 if ((*CharS & 0xC0) != 0xC0)
3354 ((test & ((unsigned char)*CharS)) != 0))
3359 if ((n > 6) || ((CharE - CharS) < n))
3366 * @brief detect whether this char starts an utf-8 encoded char
3367 * @param Char character to inspect
3368 * @returns yes or no
3370 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3372 /** 11??.???? indicates an UTF8 Sequence. */
3373 return ((Char & 0xC0) == 0xC0);
3378 * @brief measure the number of glyphs in an UTF8 string...
3379 * @param Buf string to measure
3380 * @returns the number of glyphs in Buf
3382 long StrBuf_Utf8StrLen(StrBuf *Buf)
3388 if ((Buf == NULL) || (Buf->BufUsed == 0))
3391 eptr = Buf->buf + Buf->BufUsed;
3392 while ((aptr < eptr) && (*aptr != '\0')) {
3393 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3394 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3395 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3408 * @brief cuts a string after maxlen glyphs
3409 * @param Buf string to cut to maxlen glyphs
3410 * @param maxlen how long may the string become?
3411 * @returns current length of the string
3413 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3419 eptr = Buf->buf + Buf->BufUsed;
3420 while ((aptr < eptr) && (*aptr != '\0')) {
3421 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3422 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3423 while ((*aptr++ != '\0') && (m-- > 0));
3432 Buf->BufUsed = aptr - Buf->buf;
3433 return Buf->BufUsed;
3436 return Buf->BufUsed;
3444 /*******************************************************************************
3446 *******************************************************************************/
3449 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3450 #define OS_CODE 0x03 /*< unix */
3453 * @ingroup StrBuf_DeEnCoder
3454 * @brief uses the same calling syntax as compress2(), but it
3455 * creates a stream compatible with HTTP "Content-encoding: gzip"
3456 * @param dest compressed buffer
3457 * @param destLen length of the compresed data
3458 * @param source source to encode
3459 * @param sourceLen length of source to encode
3460 * @param level compression level
3462 int ZEXPORT compress_gzip(Bytef * dest,
3464 const Bytef * source,
3468 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3470 /* write gzip header */
3471 snprintf((char *) dest, *destLen,
3472 "%c%c%c%c%c%c%c%c%c%c",
3473 gz_magic[0], gz_magic[1], Z_DEFLATED,
3474 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3477 /* normal deflate */
3480 stream.next_in = (Bytef *) source;
3481 stream.avail_in = (uInt) sourceLen;
3482 stream.next_out = dest + 10L; // after header
3483 stream.avail_out = (uInt) * destLen;
3484 if ((uLong) stream.avail_out != *destLen)
3487 stream.zalloc = (alloc_func) 0;
3488 stream.zfree = (free_func) 0;
3489 stream.opaque = (voidpf) 0;
3491 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3492 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3496 err = deflate(&stream, Z_FINISH);
3497 if (err != Z_STREAM_END) {
3498 deflateEnd(&stream);
3499 return err == Z_OK ? Z_BUF_ERROR : err;
3501 *destLen = stream.total_out + 10L;
3503 /* write CRC and Length */
3504 uLong crc = crc32(0L, source, sourceLen);
3506 for (n = 0; n < 4; ++n, ++*destLen) {
3507 dest[*destLen] = (int) (crc & 0xff);
3510 uLong len = stream.total_in;
3511 for (n = 0; n < 4; ++n, ++*destLen) {
3512 dest[*destLen] = (int) (len & 0xff);
3515 err = deflateEnd(&stream);
3522 * @ingroup StrBuf_DeEnCoder
3523 * @brief compress the buffer with gzip
3524 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3525 * @param Buf buffer whose content is to be gzipped
3527 int CompressBuffer(StrBuf *Buf)
3530 char *compressed_data = NULL;
3531 size_t compressed_len, bufsize;
3534 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3535 compressed_data = malloc(compressed_len);
3537 if (compressed_data == NULL)
3539 /* Flush some space after the used payload so valgrind shuts up... */
3540 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3541 Buf->buf[Buf->BufUsed + i++] = '\0';
3542 if (compress_gzip((Bytef *) compressed_data,
3545 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3548 Buf->buf = compressed_data;
3549 Buf->BufUsed = compressed_len;
3550 Buf->BufSize = bufsize;
3551 /* Flush some space after the used payload so valgrind shuts up... */
3553 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3554 Buf->buf[Buf->BufUsed + i++] = '\0';
3557 free(compressed_data);
3559 #endif /* HAVE_ZLIB */
3563 /*******************************************************************************
3564 * File I/O; Callbacks to libevent *
3565 *******************************************************************************/
3567 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3572 if ((FB == NULL) || (FB->Buf == NULL))
3576 * check whether the read pointer is somewhere in a range
3577 * where a cut left is inexpensive
3580 if (FB->ReadWritePointer != NULL)
3584 already_read = FB->ReadWritePointer - FB->Buf->buf;
3585 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3587 if (already_read != 0) {
3590 unread = FB->Buf->BufUsed - already_read;
3592 /* else nothing to compact... */
3594 FB->ReadWritePointer = FB->Buf->buf;
3595 bufremain = FB->Buf->BufSize;
3597 else if ((unread < 64) ||
3598 (bufremain < already_read))
3601 * if its just a tiny bit remaining, or we run out of space...
3604 FB->Buf->BufUsed = unread;
3605 if (unread < already_read)
3606 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3608 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3609 FB->ReadWritePointer = FB->Buf->buf;
3610 bufremain = FB->Buf->BufSize - unread - 1;
3612 else if (bufremain < (FB->Buf->BufSize / 10))
3614 /* get a bigger buffer */
3616 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3618 FB->ReadWritePointer = FB->Buf->buf + unread;
3620 bufremain = FB->Buf->BufSize - unread - 1;
3621 /*TODO: special increase function that won't copy the already read! */
3624 else if (bufremain < 10) {
3625 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3627 FB->ReadWritePointer = FB->Buf->buf;
3629 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3634 FB->ReadWritePointer = FB->Buf->buf;
3635 bufremain = FB->Buf->BufSize - 1;
3638 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3641 FB->Buf->BufUsed += n;
3642 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3647 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3652 if ((FB == NULL) || (FB->Buf == NULL))
3655 if (FB->ReadWritePointer != NULL)
3657 WriteRemain = FB->Buf->BufUsed -
3658 (FB->ReadWritePointer -
3662 FB->ReadWritePointer = FB->Buf->buf;
3663 WriteRemain = FB->Buf->BufUsed;
3666 n = write(fd, FB->ReadWritePointer, WriteRemain);
3668 FB->ReadWritePointer += n;
3670 if (FB->ReadWritePointer ==
3671 FB->Buf->buf + FB->Buf->BufUsed)
3673 FlushStrBuf(FB->Buf);
3674 FB->ReadWritePointer = NULL;
3677 // check whether we've got something to write
3678 // get the maximum chunk plus the pointer we can send
3679 // write whats there
3680 // if not all was sent, remember the send pointer for the next time
3681 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3687 * @ingroup StrBuf_IO
3688 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3689 * @param LineBuf your line will be copied here.
3690 * @param FB BLOB with lines of text...
3691 * @param Ptr moved arround to keep the next-line across several iterations
3692 * has to be &NULL on start; will be &NotNULL on end of buffer
3693 * @returns size of copied buffer
3695 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3697 const char *aptr, *ptr, *eptr;
3700 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3701 FB->ReadWritePointer = StrBufNOTNULL;
3705 FlushStrBuf(LineBuf);
3706 if (FB->ReadWritePointer == NULL)
3707 ptr = aptr = FB->Buf->buf;
3709 ptr = aptr = FB->ReadWritePointer;
3711 optr = LineBuf->buf;
3712 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3713 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3715 while ((ptr <= eptr) &&
3722 LineBuf->BufUsed = optr - LineBuf->buf;
3723 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3724 optr = LineBuf->buf + LineBuf->BufUsed;
3725 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3730 if (optr > LineBuf->buf)
3732 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3733 LineBuf->BufUsed = optr - LineBuf->buf;
3735 if ((FB->ReadWritePointer != NULL) &&
3736 (FB->ReadWritePointer != FB->Buf->buf))
3738 /* Ok, the client application read all the data
3739 it was interested in so far. Since there is more to read,
3740 we now shrink the buffer, and move the rest over.
3742 StrBufCutLeft(FB->Buf,
3743 FB->ReadWritePointer - FB->Buf->buf);
3744 FB->ReadWritePointer = FB->Buf->buf;
3746 return eMustReadMore;
3749 LineBuf->BufUsed = optr - LineBuf->buf;
3751 if ((ptr <= eptr) && (*ptr == '\r'))
3753 if ((ptr <= eptr) && (*ptr == '\n'))
3757 FB->ReadWritePointer = ptr;
3760 FlushStrBuf(FB->Buf);
3761 FB->ReadWritePointer = NULL;
3764 return eReadSuccess;
3768 * @ingroup StrBuf_CHUNKED_IO
3769 * @brief check whether the chunk-buffer has more data waiting or not.
3770 * @param FB Chunk-Buffer to inspect
3772 eReadState StrBufCheckBuffer(IOBuffer *FB)
3776 if (FB->Buf->BufUsed == 0)
3777 return eReadSuccess;
3778 if (FB->ReadWritePointer == NULL)
3779 return eBufferNotEmpty;
3780 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3781 return eBufferNotEmpty;
3782 return eReadSuccess;
3785 long IOBufferStrLength(IOBuffer *FB)
3787 if (FB->ReadWritePointer == NULL)
3788 return StrLength(FB->Buf);
3790 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3793 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3795 memset(FDB, 0, sizeof(FDIOBuffer));
3797 FDB->TotalSendSize = TotalSendSize;
3799 #ifndef LINUX_SENDFILE
3800 FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
3802 pipe(FDB->SplicePipe);
3807 void FDIOBufferDelete(FDIOBuffer *FDB)
3809 #ifndef LINUX_SENDFILE
3810 FreeStrBuf(&FDB->ChunkBuffer);
3812 close(FDB->SplicePipe[0]);
3813 close(FDB->SplicePipe[1]);
3816 close(FDB->OtherFD);
3817 memset(FDB, 0, sizeof(FDIOBuffer));
3820 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3825 #ifdef LINUX_SENDFILE
3827 sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3830 *Err = strerror(errno);
3833 FDB->ChunkSendRemain -= sent;
3834 FDB->TotalSentAlready += sent;
3835 return FDB->ChunkSendRemain;
3837 pRead = FDB->ChunkBuffer->buf;
3838 while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
3840 nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
3842 FDB->ChunkBuffer->BufUsed += nRead;
3843 FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
3845 else if (nRead == 0) {}
3850 nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
3853 FDB->TotalSentAlready += nRead;
3854 FDB->ChunkSendRemain -= nRead;
3855 return FDB->ChunkSendRemain;
3863 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3865 ssize_t sent, pipesize;
3867 #ifdef LINUX_SENDFILE
3869 pipesize = splice(FDB->IOB->fd, NULL,
3870 FDB->SplicePipe[1], NULL,
3871 FDB->ChunkSendRemain,
3872 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3875 *Err = strerror(errno);
3879 sent = splice(FDB->SplicePipe[0], NULL,
3880 FDB->OtherFD, &FDB->TotalSentAlready,
3881 pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
3884 *Err = strerror(errno);
3887 FDB->ChunkSendRemain -= sent;
3891 sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
3896 FDB->ChunkBuffer->BufUsed = sent;
3898 while (nWritten < FDB->ChunkBuffer->BufUsed) {
3899 rc = write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
3901 *Err = strerror(errno);
3907 FDB->ChunkBuffer->BufUsed = 0;
3908 FDB->TotalSentAlready += sent;
3909 FDB->ChunkSendRemain -= sent;
3910 return FDB->ChunkSendRemain;
3912 else if (sent < 0) {
3913 *Err = strerror(errno);
3921 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
3927 int nSuccessLess = 0;
3931 fdflags = fcntl(FDB->OtherFD, F_GETFL);
3932 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3934 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
3935 (FDB->ChunkSendRemain > 0))
3938 tv.tv_sec = 1; /* selectresolution; */
3942 FD_SET(FDB->OtherFD, &rfds);
3943 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
3944 *Error = strerror(errno);
3948 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
3953 should_write = FDB->IOB->Buf->BufUsed -
3954 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
3955 if (should_write > FDB->ChunkSendRemain)
3956 should_write = FDB->ChunkSendRemain;
3958 rlen = write(FDB->OtherFD,
3959 FDB->IOB->ReadWritePointer,
3962 *Error = strerror(errno);
3966 FDB->TotalSentAlready += rlen;
3967 FDB->IOB->ReadWritePointer += rlen;
3968 FDB->ChunkSendRemain -= rlen;
3970 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
3972 FlushStrBuf(FDB->IOB->Buf);
3973 FDB->IOB->ReadWritePointer = NULL;
3976 if (FDB->ChunkSendRemain == 0)
3977 return eReadSuccess;
3979 return eMustReadMore;
3982 /*******************************************************************************
3983 * File I/O; Prefer buffered read since its faster! *
3984 *******************************************************************************/
3987 * @ingroup StrBuf_IO
3988 * @brief Read a line from socket
3989 * flushes and closes the FD on error
3990 * @param buf the buffer to get the input to
3991 * @param fd pointer to the filedescriptor to read
3992 * @param append Append to an existing string or replace?
3993 * @param Error strerror() on error
3994 * @returns numbers of chars read
3996 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
3998 int len, rlen, slen;
4003 slen = len = buf->BufUsed;
4005 rlen = read(*fd, &buf->buf[len], 1);
4007 *Error = strerror(errno);
4014 if (buf->buf[len] == '\n')
4016 if (buf->buf[len] != '\r')
4018 if (len + 2 >= buf->BufSize) {
4020 buf->buf[len+1] = '\0';
4021 IncreaseBuf(buf, 1, -1);
4025 buf->buf[len] = '\0';
4030 * @ingroup StrBuf_BufferedIO
4031 * @brief Read a line from socket
4032 * flushes and closes the FD on error
4033 * @param Line the line to read from the fd / I/O Buffer
4034 * @param buf the buffer to get the input to
4035 * @param fd pointer to the filedescriptor to read
4036 * @param timeout number of successless selects until we bail out
4037 * @param selectresolution how long to wait on each select
4038 * @param Error strerror() on error
4039 * @returns numbers of chars read
4041 int StrBufTCP_read_buffered_line(StrBuf *Line,
4045 int selectresolution,
4049 int nSuccessLess = 0;
4056 if (buf->BufUsed > 0) {
4057 pch = strchr(buf->buf, '\n');
4060 len = pch - buf->buf;
4061 if (len > 0 && (*(pch - 1) == '\r') )
4063 StrBufSub(Line, buf, 0, len - rlen);
4064 StrBufCutLeft(buf, len + 1);
4069 if (buf->BufSize - buf->BufUsed < 10)
4070 IncreaseBuf(buf, 1, -1);
4072 fdflags = fcntl(*fd, F_GETFL);
4073 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4075 while ((nSuccessLess < timeout) && (pch == NULL)) {
4077 tv.tv_sec = selectresolution;
4082 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4083 *Error = strerror(errno);
4089 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4094 &buf->buf[buf->BufUsed],
4095 buf->BufSize - buf->BufUsed - 1);
4097 *Error = strerror(errno);
4102 else if (rlen > 0) {
4104 buf->BufUsed += rlen;
4105 buf->buf[buf->BufUsed] = '\0';
4106 if (buf->BufUsed + 10 > buf->BufSize) {
4107 IncreaseBuf(buf, 1, -1);
4109 pch = strchr(buf->buf, '\n');
4116 len = pch - buf->buf;
4117 if (len > 0 && (*(pch - 1) == '\r') )
4119 StrBufSub(Line, buf, 0, len - rlen);
4120 StrBufCutLeft(buf, len + 1);
4127 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4128 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4129 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4131 * @ingroup StrBuf_BufferedIO
4132 * @brief Read a line from socket
4133 * flushes and closes the FD on error
4134 * @param Line where to append our Line read from the fd / I/O Buffer;
4135 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4136 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4137 * @param fd pointer to the filedescriptor to read
4138 * @param timeout number of successless selects until we bail out
4139 * @param selectresolution how long to wait on each select
4140 * @param Error strerror() on error
4141 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4143 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4148 int selectresolution,
4151 const char *pche = NULL;
4152 const char *pos = NULL;
4154 int len, rlen, retlen;
4155 int nSuccessLess = 0;
4157 const char *pch = NULL;
4163 if ((Line == NULL) ||
4170 *Error = ErrRBLF_PreConditionFailed;
4175 if ((IOBuf->BufUsed > 0) &&
4177 (pos < IOBuf->buf + IOBuf->BufUsed))
4181 pche = IOBuf->buf + IOBuf->BufUsed;
4185 while ((pch < pche) && (*pch != '\n'))
4187 if (Line->BufUsed + 10 > Line->BufSize)
4190 apos = pcht - Line->buf;
4192 IncreaseBuf(Line, 1, -1);
4193 pcht = Line->buf + apos;
4201 if (len > 0 && (*(pch - 1) == '\r') )
4210 if ((pch >= pche) || (*pch == '\0'))
4218 if ((pch != NULL) &&
4221 if (pch + 1 >= pche) {
4234 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4236 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4237 IncreaseBuf(IOBuf, 1, -1);
4239 fdflags = fcntl(*fd, F_GETFL);
4240 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4243 while ((nSuccessLess < timeout) &&
4253 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4254 *Error = strerror(errno);
4258 *Error = ErrRBLF_SelectFailed;
4261 if (! FD_ISSET(*fd, &rfds) != 0) {
4267 &IOBuf->buf[IOBuf->BufUsed],
4268 IOBuf->BufSize - IOBuf->BufUsed - 1);
4270 *Error = strerror(errno);
4275 else if (rlen > 0) {
4277 pLF = IOBuf->buf + IOBuf->BufUsed;
4278 IOBuf->BufUsed += rlen;
4279 IOBuf->buf[IOBuf->BufUsed] = '\0';
4281 pche = IOBuf->buf + IOBuf->BufUsed;
4283 while ((pLF < pche) && (*pLF != '\n'))
4285 if ((pLF >= pche) || (*pLF == '\0'))
4288 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4292 if (pLF != NULL) apos = pLF - IOBuf->buf;
4293 IncreaseBuf(IOBuf, 1, -1);
4294 if (pLF != NULL) pLF = IOBuf->buf + apos;
4304 if (len > 0 && (*(pLF - 1) == '\r') )
4306 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4307 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4313 return retlen + len;
4315 *Error = ErrRBLF_NotEnoughSentFromServer;
4320 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4322 * @ingroup StrBuf_IO
4323 * @brief Input binary data from socket
4324 * flushes and closes the FD on error
4325 * @param Buf the buffer to get the input to
4326 * @param fd pointer to the filedescriptor to read
4327 * @param append Append to an existing string or replace?
4328 * @param nBytes the maximal number of bytes to read
4329 * @param Error strerror() on error
4330 * @returns numbers of chars read
4332 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4343 if ((Buf == NULL) || (*fd == -1))
4345 *Error = ErrRBLF_BLOBPreConditionFailed;
4350 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4351 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4353 ptr = Buf->buf + Buf->BufUsed;
4355 fdflags = fcntl(*fd, F_GETFL);
4356 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4358 while ((nRead < nBytes) &&
4368 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4369 *Error = strerror(errno);
4373 *Error = ErrRBLF_SelectFailed;
4376 if (! FD_ISSET(*fd, &rfds) != 0) {
4382 if ((rlen = read(*fd,
4384 nBytes - nRead)) == -1) {
4387 *Error = strerror(errno);
4392 Buf->BufUsed += rlen;
4394 Buf->buf[Buf->BufUsed] = '\0';
4398 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4399 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4401 * @ingroup StrBuf_BufferedIO
4402 * @brief Input binary data from socket
4403 * flushes and closes the FD on error
4404 * @param Blob put binary thing here
4405 * @param IOBuf the buffer to get the input to
4406 * @param Pos offset inside of IOBuf
4407 * @param fd pointer to the filedescriptor to read
4408 * @param append Append to an existing string or replace?
4409 * @param nBytes the maximal number of bytes to read
4410 * @param check whether we should search for '000\n' terminators in case of timeouts
4411 * @param Error strerror() on error
4412 * @returns numbers of chars read
4414 int StrBufReadBLOBBuffered(StrBuf *Blob,
4428 int nAlreadyRead = 0;
4433 int nSuccessLess = 0;
4436 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4440 *Error = ErrRBB_BLOBFPreConditionFailed;
4446 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4447 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4452 len = pos - IOBuf->buf;
4453 rlen = IOBuf->BufUsed - len;
4456 if ((IOBuf->BufUsed > 0) &&
4458 (pos < IOBuf->buf + IOBuf->BufUsed))
4460 if (rlen < nBytes) {
4461 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4462 Blob->BufUsed += rlen;
4463 Blob->buf[Blob->BufUsed] = '\0';
4464 nAlreadyRead = nRead = rlen;
4467 if (rlen >= nBytes) {
4468 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4469 Blob->BufUsed += nBytes;
4470 Blob->buf[Blob->BufUsed] = '\0';
4471 if (rlen == nBytes) {
4483 if (IOBuf->BufSize < nBytes - nRead)
4484 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4487 len = Blob->BufUsed;
4489 fdflags = fcntl(*fd, F_GETFL);
4490 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4498 while ((nSuccessLess < MaxTries) &&
4508 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4509 *Error = strerror(errno);
4513 *Error = ErrRBLF_SelectFailed;
4516 if (! FD_ISSET(*fd, &rfds) != 0) {
4523 IOBuf->BufSize - (ptr - IOBuf->buf));
4527 *Error = strerror(errno);
4530 else if (rlen == 0){
4531 if ((check == NNN_TERM) &&
4533 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4535 StrBufPlain(Blob, HKEY("\n000\n"));
4536 StrBufCutRight(Blob, 5);
4537 return Blob->BufUsed;
4539 else if (!IsNonBlock)
4541 else if (nSuccessLess > MaxTries) {
4543 *Error = ErrRBB_too_many_selects;
4547 else if (rlen > 0) {
4551 IOBuf->BufUsed += rlen;
4554 if (nSuccessLess >= MaxTries) {
4556 *Error = ErrRBB_too_many_selects;
4560 if (nRead > nBytes) {
4561 *Pos = IOBuf->buf + nBytes;
4563 Blob->buf[Blob->BufUsed] = '\0';
4564 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4568 return nRead + nAlreadyRead;
4572 * @ingroup StrBuf_IO
4573 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4574 * @param LineBuf your line will be copied here.
4575 * @param Buf BLOB with lines of text...
4576 * @param Ptr moved arround to keep the next-line across several iterations
4577 * has to be &NULL on start; will be &NotNULL on end of buffer
4578 * @returns size of remaining buffer
4580 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4582 const char *aptr, *ptr, *eptr;
4585 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4586 *Ptr = StrBufNOTNULL;
4590 FlushStrBuf(LineBuf);
4592 ptr = aptr = Buf->buf;
4596 optr = LineBuf->buf;
4597 eptr = Buf->buf + Buf->BufUsed;
4598 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4600 while ((ptr <= eptr) &&
4607 LineBuf->BufUsed = optr - LineBuf->buf;
4608 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4609 optr = LineBuf->buf + LineBuf->BufUsed;
4610 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4614 if ((ptr >= eptr) && (optr > LineBuf->buf))
4616 LineBuf->BufUsed = optr - LineBuf->buf;
4618 if ((ptr <= eptr) && (*ptr == '\r'))
4620 if ((ptr <= eptr) && (*ptr == '\n'))
4627 *Ptr = StrBufNOTNULL;
4630 return Buf->BufUsed - (ptr - Buf->buf);
4635 * @ingroup StrBuf_IO
4636 * @brief removes double slashes from pathnames
4637 * @param Dir directory string to filter
4638 * @param RemoveTrailingSlash allows / disallows trailing slashes
4640 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4646 while (!IsEmptyStr(a)) {
4658 if ((RemoveTrailingSlash) &&
4664 Dir->BufUsed = b - Dir->buf;