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 <bits/fcntl.h>
34 #include <sys/sendfile.h>
36 #include "libcitadel.h"
47 #include <sys/sendfile.h>
52 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
53 const Bytef * source, uLong sourceLen, int level);
55 int BaseStrBufSize = 64;
57 const char *StrBufNOTNULL = ((char*) NULL) - 1;
59 const char HexList[256][3] = {
60 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
61 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
62 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
63 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
64 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
65 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
66 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
67 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
68 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
69 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
70 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
71 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
72 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
73 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
74 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
75 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
78 * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
79 * StrBuf is a versatile class, aiding the handling of dynamic strings
80 * * reduce de/reallocations
81 * * reduce the need to remeasure it
82 * * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
83 * * allow asyncroneous IO for line and Blob based operations
84 * * reduce the use of memove in those
85 * * Quick filling in several operations with append functions
89 * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
94 * @defgroup StrBuf_Cast Cast operators to interact with char* based code
96 * use these operators to interfere with code demanding char*;
97 * if you need to own the content, smash me. Avoid, since we loose the length information.
101 * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
103 * operations to get your Strings into a StrBuf, manipulating them, or appending
106 * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence
108 * Quick tokenizer; demands of the user to pull its tokens in sequence
112 * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
114 * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
118 * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
120 * File IO to fill StrBufs; Works with work-buffer shared across several calls;
121 * External Cursor to maintain the current read position inside of the buffer
122 * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower)
126 * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
132 * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
134 * these functions translate the content of a buffer into another representation;
135 * some are combined Fillers and encoders
139 * Private Structure for the Stringbuffer
142 char *buf; /**< the pointer to the dynamic buffer */
143 long BufSize; /**< how many spcae do we optain */
144 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
145 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
147 long nIncreases; /**< for profiling; cound how many times we needed more */
148 char bt [SIZ]; /**< Stacktrace of last increase */
149 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
154 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
155 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
158 #ifdef HAVE_BACKTRACE
159 static void StrBufBacktrace(StrBuf *Buf, int which)
163 void *stack_frames[50];
168 pstart = pch = Buf->bt;
170 pstart = pch = Buf->bt_lastinc;
171 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
172 strings = backtrace_symbols(stack_frames, size);
173 for (i = 0; i < size; i++) {
175 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
177 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
186 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
188 if (hFreeDbglog == -1){
189 pid_t pid = getpid();
191 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
192 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
194 if ((*FreeMe)->nIncreases > 0)
198 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
200 (*FreeMe)->nIncreases,
204 (*FreeMe)->bt_lastinc);
205 n = write(hFreeDbglog, buf, n);
211 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
215 n = write(hFreeDbglog, buf, n);
219 void dbg_IncreaseBuf(StrBuf *IncMe)
222 #ifdef HAVE_BACKTRACE
223 StrBufBacktrace(Buf, 1);
227 void dbg_Init(StrBuf *Buf)
231 Buf->bt_lastinc[0] = '\0';
232 #ifdef HAVE_BACKTRACE
233 StrBufBacktrace(Buf, 0);
239 #define dbg_FreeStrBuf(a, b)
240 #define dbg_IncreaseBuf(a)
247 * @brief swaps the contents of two StrBufs
248 * this is to be used to have cheap switched between a work-buffer and a target buffer
250 * @param B second one
252 static inline void SwapBuffers(StrBuf *A, StrBuf *B)
256 memcpy(&C, A, sizeof(*A));
257 memcpy(A, B, sizeof(*B));
258 memcpy(B, &C, sizeof(C));
263 * @ingroup StrBuf_Cast
264 * @brief Cast operator to Plain String
265 * @note if the buffer is altered by StrBuf operations, this pointer may become
266 * invalid. So don't lean on it after altering the buffer!
267 * Since this operation is considered cheap, rather call it often than risking
268 * your pointer to become invalid!
269 * @param Str the string we want to get the c-string representation for
270 * @returns the Pointer to the Content. Don't mess with it!
272 inline const char *ChrPtr(const StrBuf *Str)
280 * @ingroup StrBuf_Cast
281 * @brief since we know strlen()'s result, provide it here.
282 * @param Str the string to return the length to
283 * @returns contentlength of the buffer
285 inline int StrLength(const StrBuf *Str)
287 return (Str != NULL) ? Str->BufUsed : 0;
291 * @ingroup StrBuf_DeConstructors
292 * @brief local utility function to resize the buffer
293 * @param Buf the buffer whichs storage we should increase
294 * @param KeepOriginal should we copy the original buffer or just start over with a new one
295 * @param DestSize what should fit in after?
297 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
300 size_t NewSize = Buf->BufSize * 2;
306 while ((NewSize <= DestSize) && (NewSize != 0))
312 NewBuf= (char*) malloc(NewSize);
316 if (KeepOriginal && (Buf->BufUsed > 0))
318 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
327 Buf->BufSize = NewSize;
329 dbg_IncreaseBuf(Buf);
335 * @ingroup StrBuf_DeConstructors
336 * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
337 * @param Buf Buffer to shrink (has to be empty)
338 * @param ThreshHold if the buffer is bigger then this, its readjusted
339 * @param NewSize if we Shrink it, how big are we going to be afterwards?
341 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
344 (Buf->BufUsed == 0) &&
345 (Buf->BufSize < ThreshHold)) {
347 Buf->buf = (char*) malloc(NewSize);
349 Buf->BufSize = NewSize;
354 * @ingroup StrBuf_DeConstructors
355 * @brief shrink long term buffers to their real size so they don't waste memory
356 * @param Buf buffer to shrink
357 * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
358 * @returns physical size of the buffer
360 long StrBufShrinkToFit(StrBuf *Buf, int Force)
365 (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
367 char *TmpBuf = (char*) malloc(Buf->BufUsed + 1);
368 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
369 Buf->BufSize = Buf->BufUsed + 1;
377 * @ingroup StrBuf_DeConstructors
378 * @brief Allocate a new buffer with default buffer size
379 * @returns the new stringbuffer
381 StrBuf* NewStrBuf(void)
385 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
386 NewBuf->buf = (char*) malloc(BaseStrBufSize);
387 NewBuf->buf[0] = '\0';
388 NewBuf->BufSize = BaseStrBufSize;
390 NewBuf->ConstBuf = 0;
398 * @ingroup StrBuf_DeConstructors
399 * @brief Copy Constructor; returns a duplicate of CopyMe
400 * @param CopyMe Buffer to faxmilate
401 * @returns the new stringbuffer
403 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
410 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
411 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
412 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
413 NewBuf->BufUsed = CopyMe->BufUsed;
414 NewBuf->BufSize = CopyMe->BufSize;
415 NewBuf->ConstBuf = 0;
423 * @ingroup StrBuf_DeConstructors
424 * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
425 * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
426 * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
427 * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
428 * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
429 * @returns the new stringbuffer
431 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
435 if (CreateRelpaceMe == NULL)
440 if (*CreateRelpaceMe != NULL)
441 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
443 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
447 if (CopyFlushMe == NULL)
449 if (*CreateRelpaceMe != NULL)
450 FlushStrBuf(*CreateRelpaceMe);
452 *CreateRelpaceMe = NewStrBuf();
457 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
458 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
459 * be a big IO-Buffer...
461 if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
463 if (*CreateRelpaceMe == NULL)
465 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
470 NewBuf = *CreateRelpaceMe;
473 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
477 if (*CreateRelpaceMe == NULL)
479 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
483 NewBuf = *CreateRelpaceMe;
484 SwapBuffers (NewBuf, CopyFlushMe);
487 FlushStrBuf(CopyFlushMe);
492 * @ingroup StrBuf_DeConstructors
493 * @brief create a new Buffer using an existing c-string
494 * this function should also be used if you want to pre-suggest
495 * the buffer size to allocate in conjunction with ptr == NULL
496 * @param ptr the c-string to copy; may be NULL to create a blank instance
497 * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
498 * @returns the new stringbuffer
500 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
503 size_t Siz = BaseStrBufSize;
506 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
508 CopySize = strlen((ptr != NULL)?ptr:"");
512 while ((Siz <= CopySize) && (Siz != 0))
520 NewBuf->buf = (char*) malloc(Siz);
521 if (NewBuf->buf == NULL)
526 NewBuf->BufSize = Siz;
528 memcpy(NewBuf->buf, ptr, CopySize);
529 NewBuf->buf[CopySize] = '\0';
530 NewBuf->BufUsed = CopySize;
533 NewBuf->buf[0] = '\0';
536 NewBuf->ConstBuf = 0;
544 * @ingroup StrBuf_DeConstructors
545 * @brief Set an existing buffer from a c-string
546 * @param Buf buffer to load
547 * @param ptr c-string to put into
548 * @param nChars set to -1 if we should work 0-terminated
549 * @returns the new length of the string
551 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
566 CopySize = strlen(ptr);
570 while ((Siz <= CopySize) && (Siz != 0))
578 if (Siz != Buf->BufSize)
579 IncreaseBuf(Buf, 0, Siz);
580 memcpy(Buf->buf, ptr, CopySize);
581 Buf->buf[CopySize] = '\0';
582 Buf->BufUsed = CopySize;
589 * @ingroup StrBuf_DeConstructors
590 * @brief use strbuf as wrapper for a string constant for easy handling
591 * @param StringConstant a string to wrap
592 * @param SizeOfStrConstant should be sizeof(StringConstant)-1
594 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
598 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
599 NewBuf->buf = (char*) StringConstant;
600 NewBuf->BufSize = SizeOfStrConstant;
601 NewBuf->BufUsed = SizeOfStrConstant;
602 NewBuf->ConstBuf = 1;
611 * @ingroup StrBuf_DeConstructors
612 * @brief flush the content of a Buf; keep its struct
613 * @param buf Buffer to flush
615 int FlushStrBuf(StrBuf *buf)
627 * @ingroup StrBuf_DeConstructors
628 * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
629 * @param buf Buffer to wipe
631 int FLUSHStrBuf(StrBuf *buf)
637 if (buf->BufUsed > 0) {
638 memset(buf->buf, 0, buf->BufUsed);
645 int hFreeDbglog = -1;
648 * @ingroup StrBuf_DeConstructors
649 * @brief Release a Buffer
650 * Its a double pointer, so it can NULL your pointer
651 * so fancy SIG11 appear instead of random results
652 * @param FreeMe Pointer Pointer to the buffer to free
654 void FreeStrBuf (StrBuf **FreeMe)
659 dbg_FreeStrBuf(FreeMe, 'F');
661 if (!(*FreeMe)->ConstBuf)
662 free((*FreeMe)->buf);
668 * @ingroup StrBuf_DeConstructors
669 * @brief flatten a Buffer to the Char * we return
670 * Its a double pointer, so it can NULL your pointer
671 * so fancy SIG11 appear instead of random results
672 * The Callee then owns the buffer and is responsible for freeing it.
673 * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
674 * @returns the pointer of the buffer; Callee owns the memory thereafter.
676 char *SmashStrBuf (StrBuf **SmashMe)
680 if ((SmashMe == NULL) || (*SmashMe == NULL))
683 dbg_FreeStrBuf(SmashMe, 'S');
685 Ret = (*SmashMe)->buf;
692 * @ingroup StrBuf_DeConstructors
693 * @brief Release the buffer
694 * If you want put your StrBuf into a Hash, use this as Destructor.
695 * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
697 void HFreeStrBuf (void *VFreeMe)
699 StrBuf *FreeMe = (StrBuf*)VFreeMe;
703 dbg_FreeStrBuf(SmashMe, 'H');
705 if (!FreeMe->ConstBuf)
711 /*******************************************************************************
712 * Simple string transformations *
713 *******************************************************************************/
717 * @brief Wrapper around atol
719 long StrTol(const StrBuf *Buf)
724 return atol(Buf->buf);
731 * @brief Wrapper around atoi
733 int StrToi(const StrBuf *Buf)
737 if (Buf->BufUsed > 0)
738 return atoi(Buf->buf);
745 * @brief Checks to see if the string is a pure number
746 * @param Buf The buffer to inspect
747 * @returns 1 if its a pure number, 0, if not.
749 int StrBufIsNumber(const StrBuf *Buf) {
751 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
754 strtoll(Buf->buf, &pEnd, 10);
755 if (pEnd == Buf->buf)
757 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
759 if (Buf->buf == pEnd)
765 * @ingroup StrBuf_Filler
766 * @brief modifies a Single char of the Buf
767 * You can point to it via char* or a zero-based integer
768 * @param Buf The buffer to manipulate
769 * @param ptr char* to zero; use NULL if unused
770 * @param nThChar zero based pointer into the string; use -1 if unused
771 * @param PeekValue The Character to place into the position
773 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
778 nThChar = ptr - Buf->buf;
779 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
781 Buf->buf[nThChar] = PeekValue;
786 * @ingroup StrBuf_Filler
787 * @brief modifies a range of chars of the Buf
788 * You can point to it via char* or a zero-based integer
789 * @param Buf The buffer to manipulate
790 * @param ptr char* to zero; use NULL if unused
791 * @param nThChar zero based pointer into the string; use -1 if unused
792 * @param nChars how many chars are to be flushed?
793 * @param PookValue The Character to place into that area
795 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
800 nThChar = ptr - Buf->buf;
801 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
803 if (nThChar + nChars > Buf->BufUsed)
804 nChars = Buf->BufUsed - nThChar;
806 memset(Buf->buf + nThChar, PookValue, nChars);
807 /* just to be shure... */
808 Buf->buf[Buf->BufUsed] = 0;
813 * @ingroup StrBuf_Filler
814 * @brief Append a StringBuffer to the buffer
815 * @param Buf Buffer to modify
816 * @param AppendBuf Buffer to copy at the end of our buffer
817 * @param Offset Should we start copying from an offset?
819 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
821 if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
824 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
827 AppendBuf->BufUsed + Buf->BufUsed);
829 memcpy(Buf->buf + Buf->BufUsed,
830 AppendBuf->buf + Offset,
831 AppendBuf->BufUsed - Offset);
832 Buf->BufUsed += AppendBuf->BufUsed - Offset;
833 Buf->buf[Buf->BufUsed] = '\0';
838 * @ingroup StrBuf_Filler
839 * @brief Append a C-String to the buffer
840 * @param Buf Buffer to modify
841 * @param AppendBuf Buffer to copy at the end of our buffer
842 * @param AppendSize number of bytes to copy; set to -1 if we should count it in advance
843 * @param Offset Should we start copying from an offset?
845 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset)
848 long BufSizeRequired;
850 if ((AppendBuf == NULL) || (Buf == NULL))
854 aps = strlen(AppendBuf + Offset);
856 aps = AppendSize - Offset;
858 BufSizeRequired = Buf->BufUsed + aps + 1;
859 if (Buf->BufSize <= BufSizeRequired)
860 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
862 memcpy(Buf->buf + Buf->BufUsed,
866 Buf->buf[Buf->BufUsed] = '\0';
870 * @ingroup StrBuf_Filler
871 * @brief sprintf like function appending the formated string to the buffer
872 * vsnprintf version to wrap into own calls
873 * @param Buf Buffer to extend by format and Params
874 * @param format printf alike format to add
875 * @param ap va_list containing the items for format
877 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
885 if ((Buf == NULL) || (format == NULL))
888 BufSize = Buf->BufSize;
889 nWritten = Buf->BufSize + 1;
890 Offset = Buf->BufUsed;
891 newused = Offset + nWritten;
893 while (newused >= BufSize) {
895 nWritten = vsnprintf(Buf->buf + Offset,
896 Buf->BufSize - Offset,
899 newused = Offset + nWritten;
900 if (newused >= Buf->BufSize) {
901 if (IncreaseBuf(Buf, 1, newused) == -1)
902 return; /* TODO: error handling? */
903 newused = Buf->BufSize + 1;
906 Buf->BufUsed = Offset + nWritten;
907 BufSize = Buf->BufSize;
914 * @ingroup StrBuf_Filler
915 * @brief sprintf like function appending the formated string to the buffer
916 * @param Buf Buffer to extend by format and Params
917 * @param format printf alike format to add
919 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
927 if ((Buf == NULL) || (format == NULL))
930 BufSize = Buf->BufSize;
931 nWritten = Buf->BufSize + 1;
932 Offset = Buf->BufUsed;
933 newused = Offset + nWritten;
935 while (newused >= BufSize) {
936 va_start(arg_ptr, format);
937 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
938 Buf->BufSize - Buf->BufUsed,
941 newused = Buf->BufUsed + nWritten;
942 if (newused >= Buf->BufSize) {
943 if (IncreaseBuf(Buf, 1, newused) == -1)
944 return; /* TODO: error handling? */
945 newused = Buf->BufSize + 1;
948 Buf->BufUsed += nWritten;
949 BufSize = Buf->BufSize;
956 * @ingroup StrBuf_Filler
957 * @brief sprintf like function putting the formated string into the buffer
958 * @param Buf Buffer to extend by format and Parameters
959 * @param format printf alike format to add
961 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
966 if ((Buf == NULL) || (format == NULL))
969 nWritten = Buf->BufSize + 1;
970 while (nWritten >= Buf->BufSize) {
971 va_start(arg_ptr, format);
972 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
974 if (nWritten >= Buf->BufSize) {
975 if (IncreaseBuf(Buf, 0, 0) == -1)
976 return; /* TODO: error handling? */
977 nWritten = Buf->BufSize + 1;
980 Buf->BufUsed = nWritten ;
985 * @ingroup StrBuf_Filler
986 * @brief Callback for cURL to append the webserver reply to a buffer
987 * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
988 * @param size pre-defined by the cURL API; see man 3 curl for mre info
989 * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
990 * @param stream pre-defined by the cURL API; see man 3 curl for mre info
992 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1001 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1002 return size * nmemb;
1008 * @brief extracts a substring from Source into dest
1009 * @param dest buffer to place substring into
1010 * @param Source string to copy substring from
1011 * @param Offset chars to skip from start
1012 * @param nChars number of chars to copy
1013 * @returns the number of chars copied; may be different from nChars due to the size of Source
1015 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1017 size_t NCharsRemain;
1018 if (Offset > Source->BufUsed)
1024 if (Offset + nChars < Source->BufUsed)
1026 if (nChars >= dest->BufSize)
1027 IncreaseBuf(dest, 0, nChars + 1);
1028 memcpy(dest->buf, Source->buf + Offset, nChars);
1029 dest->BufUsed = nChars;
1030 dest->buf[dest->BufUsed] = '\0';
1033 NCharsRemain = Source->BufUsed - Offset;
1034 if (NCharsRemain >= dest->BufSize)
1035 IncreaseBuf(dest, 0, NCharsRemain + 1);
1036 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1037 dest->BufUsed = NCharsRemain;
1038 dest->buf[dest->BufUsed] = '\0';
1039 return NCharsRemain;
1044 * @brief Cut nChars from the start of the string
1045 * @param Buf Buffer to modify
1046 * @param nChars how many chars should be skipped?
1048 void StrBufCutLeft(StrBuf *Buf, int nChars)
1050 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1051 if (nChars >= Buf->BufUsed) {
1055 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1056 Buf->BufUsed -= nChars;
1057 Buf->buf[Buf->BufUsed] = '\0';
1062 * @brief Cut the trailing n Chars from the string
1063 * @param Buf Buffer to modify
1064 * @param nChars how many chars should be trunkated?
1066 void StrBufCutRight(StrBuf *Buf, int nChars)
1068 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1069 if (nChars >= Buf->BufUsed) {
1073 Buf->BufUsed -= nChars;
1074 Buf->buf[Buf->BufUsed] = '\0';
1079 * @brief Cut the string after n Chars
1080 * @param Buf Buffer to modify
1081 * @param AfternChars after how many chars should we trunkate the string?
1082 * @param At if non-null and points inside of our string, cut it there.
1084 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1086 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1088 AfternChars = At - Buf->buf;
1091 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1093 Buf->BufUsed = AfternChars;
1094 Buf->buf[Buf->BufUsed] = '\0';
1100 * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1101 * @param Buf the string to modify
1103 void StrBufTrim(StrBuf *Buf)
1106 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1108 while ((Buf->BufUsed > 0) &&
1109 isspace(Buf->buf[Buf->BufUsed - 1]))
1113 Buf->buf[Buf->BufUsed] = '\0';
1115 if (Buf->BufUsed == 0) return;
1117 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1120 if (delta > 0) StrBufCutLeft(Buf, delta);
1124 * @brief changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1125 * @param Buf the string to modify
1127 void StrBufSpaceToBlank(StrBuf *Buf)
1131 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1134 pche = pch + Buf->BufUsed;
1143 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1151 pLeft = pBuff = Buf->buf;
1152 while (pBuff != NULL) {
1154 pBuff = strchr(pBuff, leftboundary);
1163 pRight = strchr(pBuff, rightboundary);
1165 StrBufCutAt(Buf, 0, pRight);
1167 StrBufCutLeft(Buf, pLeft - Buf->buf);
1172 * @ingroup StrBuf_Filler
1173 * @brief uppercase the contents of a buffer
1174 * @param Buf the buffer to translate
1176 void StrBufUpCase(StrBuf *Buf)
1180 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1183 pche = pch + Buf->BufUsed;
1184 while (pch < pche) {
1185 *pch = toupper(*pch);
1192 * @ingroup StrBuf_Filler
1193 * @brief lowercase the contents of a buffer
1194 * @param Buf the buffer to translate
1196 void StrBufLowerCase(StrBuf *Buf)
1200 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1203 pche = pch + Buf->BufUsed;
1204 while (pch < pche) {
1205 *pch = tolower(*pch);
1211 /*******************************************************************************
1212 * a tokenizer that kills, maims, and destroys *
1213 *******************************************************************************/
1216 * @ingroup StrBuf_Tokenizer
1217 * @brief Replace a token at a given place with a given length by another token with given length
1218 * @param Buf String where to work on
1219 * @param where where inside of the Buf is the search-token
1220 * @param HowLong How long is the token to be replaced
1221 * @param Repl Token to insert at 'where'
1222 * @param ReplLen Length of repl
1223 * @returns -1 if fail else length of resulting Buf
1225 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1226 const char *Repl, long ReplLen)
1229 if ((Buf == NULL) ||
1230 (where > Buf->BufUsed) ||
1231 (where + HowLong > Buf->BufUsed))
1234 if (where + ReplLen - HowLong > Buf->BufSize)
1235 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1238 memmove(Buf->buf + where + ReplLen,
1239 Buf->buf + where + HowLong,
1240 Buf->BufUsed - where - HowLong);
1242 memcpy(Buf->buf + where,
1245 Buf->BufUsed += ReplLen - HowLong;
1247 return Buf->BufUsed;
1251 * @ingroup StrBuf_Tokenizer
1252 * @brief Counts the numbmer of tokens in a buffer
1253 * @param source String to count tokens in
1254 * @param tok Tokenizer char to count
1255 * @returns numbers of tokenizer chars found
1257 int StrBufNum_tokens(const StrBuf *source, char tok)
1261 if ((source == NULL) || (source->BufUsed == 0))
1263 if ((source->BufUsed == 1) && (*source->buf == tok))
1267 pche = pch + source->BufUsed;
1278 * @ingroup StrBuf_Tokenizer
1279 * @brief a string tokenizer
1280 * @param Source StringBuffer to read into
1281 * @param parmnum n'th Parameter to remove
1282 * @param separator tokenizer character
1283 * @returns -1 if not found, else length of token.
1285 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1288 char *d, *s, *end; /* dest, source */
1291 /* Find desired @parameter */
1292 end = Source->buf + Source->BufUsed;
1294 while ((d <= end) &&
1297 /* End of string, bail! */
1302 if (*d == separator) {
1307 if ((d == NULL) || (d >= end))
1308 return 0; /* @Parameter not found */
1310 /* Find next @parameter */
1312 while ((s <= end) &&
1313 (*s && *s != separator))
1317 if (*s == separator)
1321 /* Hack and slash */
1326 memmove(d, s, Source->BufUsed - (s - Source->buf));
1327 Source->BufUsed += ReducedBy;
1328 Source->buf[Source->BufUsed] = '\0';
1330 else if (d == Source->buf) {
1332 Source->BufUsed = 0;
1336 Source->BufUsed += ReducedBy;
1349 * @ingroup StrBuf_Tokenizer
1350 * @brief a string tokenizer
1351 * @param dest Destination StringBuffer
1352 * @param Source StringBuffer to read into
1353 * @param parmnum n'th Parameter to extract
1354 * @param separator tokenizer character
1355 * @returns -1 if not found, else length of token.
1357 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1359 const char *s, *e; //* source * /
1360 int len = 0; //* running total length of extracted string * /
1361 int current_token = 0; //* token currently being processed * /
1364 dest->buf[0] = '\0';
1370 if ((Source == NULL) || (Source->BufUsed ==0)) {
1374 e = s + Source->BufUsed;
1377 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1379 while ((s < e) && !IsEmptyStr(s)) {
1380 if (*s == separator) {
1383 if (len >= dest->BufSize) {
1384 dest->BufUsed = len;
1385 if (IncreaseBuf(dest, 1, -1) < 0) {
1390 if ( (current_token == parmnum) &&
1391 (*s != separator)) {
1392 dest->buf[len] = *s;
1395 else if (current_token > parmnum) {
1401 dest->buf[len] = '\0';
1402 dest->BufUsed = len;
1404 if (current_token < parmnum) {
1405 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1408 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1417 * @ingroup StrBuf_Tokenizer
1418 * @brief a string tokenizer to fetch an integer
1419 * @param Source String containing tokens
1420 * @param parmnum n'th Parameter to extract
1421 * @param separator tokenizer character
1422 * @returns 0 if not found, else integer representation of the token
1424 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1434 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1441 * @ingroup StrBuf_Tokenizer
1442 * @brief a string tokenizer to fetch a long integer
1443 * @param Source String containing tokens
1444 * @param parmnum n'th Parameter to extract
1445 * @param separator tokenizer character
1446 * @returns 0 if not found, else long integer representation of the token
1448 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1458 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1466 * @ingroup StrBuf_Tokenizer
1467 * @brief a string tokenizer to fetch an unsigned long
1468 * @param Source String containing tokens
1469 * @param parmnum n'th Parameter to extract
1470 * @param separator tokenizer character
1471 * @returns 0 if not found, else unsigned long representation of the token
1473 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1484 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1488 return (unsigned long) atol(pnum);
1497 * @ingroup StrBuf_NextTokenizer
1498 * @brief a string tokenizer; Bounds checker
1499 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1500 * @param Source our tokenbuffer
1501 * @param pStart the token iterator pointer to inspect
1502 * @returns whether the revolving pointer is inside of the search range
1504 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1506 if ((Source == NULL) ||
1507 (*pStart == StrBufNOTNULL) ||
1508 (Source->BufUsed == 0))
1512 if (*pStart == NULL)
1516 else if (*pStart > Source->buf + Source->BufUsed)
1520 else if (*pStart <= Source->buf)
1529 * @ingroup StrBuf_NextTokenizer
1530 * @brief a string tokenizer
1531 * @param dest Destination StringBuffer
1532 * @param Source StringBuffer to read into
1533 * @param pStart pointer to the end of the last token. Feed with NULL on start.
1534 * @param separator tokenizer
1535 * @returns -1 if not found, else length of token.
1537 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1539 const char *s; /* source */
1540 const char *EndBuffer; /* end stop of source buffer */
1541 int current_token = 0; /* token currently being processed */
1542 int len = 0; /* running total length of extracted string */
1544 if ((Source == NULL) ||
1545 (Source->BufUsed == 0) )
1547 *pStart = StrBufNOTNULL;
1553 EndBuffer = Source->buf + Source->BufUsed;
1557 dest->buf[0] = '\0';
1562 *pStart = EndBuffer + 1;
1566 if (*pStart == NULL)
1568 *pStart = Source->buf; /* we're starting to examine this buffer. */
1570 else if ((*pStart < Source->buf) ||
1571 (*pStart > EndBuffer ) )
1573 return -1; /* no more tokens to find. */
1577 /* start to find the next token */
1578 while ((s <= EndBuffer) &&
1579 (current_token == 0) )
1581 if (*s == separator)
1583 /* we found the next token */
1587 if (len >= dest->BufSize)
1589 /* our Dest-buffer isn't big enough, increase it. */
1590 dest->BufUsed = len;
1592 if (IncreaseBuf(dest, 1, -1) < 0) {
1593 /* WHUT? no more mem? bail out. */
1600 if ( (current_token == 0 ) && /* are we in our target token? */
1601 (!IsEmptyStr(s) ) &&
1602 (separator != *s) ) /* don't copy the token itself */
1604 dest->buf[len] = *s; /* Copy the payload */
1605 ++len; /* remember the bigger size. */
1611 /* did we reach the end? */
1612 if ((s > EndBuffer)) {
1613 EndBuffer = StrBufNOTNULL;
1614 *pStart = EndBuffer;
1617 *pStart = s; /* remember the position for the next run */
1620 /* sanitize our extracted token */
1621 dest->buf[len] = '\0';
1622 dest->BufUsed = len;
1629 * @ingroup StrBuf_NextTokenizer
1630 * @brief a string tokenizer
1631 * @param Source StringBuffer to read from
1632 * @param pStart pointer to the end of the last token. Feed with NULL.
1633 * @param separator tokenizer character
1634 * @param nTokens number of tokens to fastforward over
1635 * @returns -1 if not found, else length of token.
1637 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1639 const char *s, *EndBuffer; //* source * /
1640 int len = 0; //* running total length of extracted string * /
1641 int current_token = 0; //* token currently being processed * /
1643 if ((Source == NULL) ||
1644 (Source->BufUsed ==0)) {
1648 return Source->BufUsed;
1650 if (*pStart == NULL)
1651 *pStart = Source->buf;
1653 EndBuffer = Source->buf + Source->BufUsed;
1655 if ((*pStart < Source->buf) ||
1656 (*pStart > EndBuffer)) {
1664 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1666 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1667 if (*s == separator) {
1670 if (current_token >= nTokens) {
1682 * @ingroup StrBuf_NextTokenizer
1683 * @brief a string tokenizer to fetch an integer
1684 * @param Source StringBuffer to read from
1685 * @param pStart Cursor on the tokenstring
1686 * @param separator tokenizer character
1687 * @returns 0 if not found, else integer representation of the token
1689 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1699 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1706 * @ingroup StrBuf_NextTokenizer
1707 * @brief a string tokenizer to fetch a long integer
1708 * @param Source StringBuffer to read from
1709 * @param pStart Cursor on the tokenstring
1710 * @param separator tokenizer character
1711 * @returns 0 if not found, else long integer representation of the token
1713 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1723 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1731 * @ingroup StrBuf_NextTokenizer
1732 * @brief a string tokenizer to fetch an unsigned long
1733 * @param Source StringBuffer to read from
1734 * @param pStart Cursor on the tokenstring
1735 * @param separator tokenizer character
1736 * @returns 0 if not found, else unsigned long representation of the token
1738 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1749 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1753 return (unsigned long) atol(pnum);
1763 /*******************************************************************************
1764 * Escape Appending *
1765 *******************************************************************************/
1768 * @ingroup StrBuf_DeEnCoder
1769 * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1770 * @param OutBuf the output buffer
1771 * @param In Buffer to encode
1772 * @param PlainIn way in from plain old c strings
1774 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1776 const char *pch, *pche;
1780 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1782 if (PlainIn != NULL) {
1783 len = strlen(PlainIn);
1789 pche = pch + In->BufUsed;
1796 pt = OutBuf->buf + OutBuf->BufUsed;
1797 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1799 while (pch < pche) {
1801 IncreaseBuf(OutBuf, 1, -1);
1802 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1803 pt = OutBuf->buf + OutBuf->BufUsed;
1806 if((*pch >= 'a' && *pch <= 'z') ||
1807 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1808 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1809 (*pch == '!') || (*pch == '_') ||
1810 (*pch == ',') || (*pch == '.'))
1817 *(pt + 1) = HexList[(unsigned char)*pch][0];
1818 *(pt + 2) = HexList[(unsigned char)*pch][1];
1820 OutBuf->BufUsed += 3;
1828 * @ingroup StrBuf_DeEnCoder
1829 * @brief append a string in hex encoding to the buffer
1830 * @param OutBuf the output buffer
1831 * @param In Buffer to encode
1832 * @param PlainIn way in from plain old c strings
1833 * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1835 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1837 const unsigned char *pch, *pche;
1841 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1843 if (PlainIn != NULL) {
1845 len = strlen((const char*)PlainIn);
1852 pch = (const unsigned char*)In->buf;
1853 pche = pch + In->BufUsed;
1860 pt = OutBuf->buf + OutBuf->BufUsed;
1861 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1863 while (pch < pche) {
1865 IncreaseBuf(OutBuf, 1, -1);
1866 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1867 pt = OutBuf->buf + OutBuf->BufUsed;
1870 *pt = HexList[*pch][0];
1872 *pt = HexList[*pch][1];
1873 pt ++; pch ++; OutBuf->BufUsed += 2;
1879 * @ingroup StrBuf_DeEnCoder
1880 * @brief append a string in hex encoding to the buffer
1881 * @param OutBuf the output buffer
1882 * @param In Buffer to encode
1883 * @param PlainIn way in from plain old c strings
1885 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1887 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1891 * @ingroup StrBuf_DeEnCoder
1892 * @brief Append a string, escaping characters which have meaning in HTML.
1894 * @param Target target buffer
1895 * @param Source source buffer; set to NULL if you just have a C-String
1896 * @param PlainIn Plain-C string to append; set to NULL if unused
1897 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
1898 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
1899 * if set to 2, linebreaks are replaced by <br/>
1901 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1903 const char *aptr, *eiptr;
1907 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1910 if (PlainIn != NULL) {
1912 len = strlen(PlainIn);
1917 eiptr = aptr + Source->BufUsed;
1918 len = Source->BufUsed;
1924 bptr = Target->buf + Target->BufUsed;
1925 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1927 while (aptr < eiptr){
1929 IncreaseBuf(Target, 1, -1);
1930 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1931 bptr = Target->buf + Target->BufUsed;
1934 memcpy(bptr, "<", 4);
1936 Target->BufUsed += 4;
1938 else if (*aptr == '>') {
1939 memcpy(bptr, ">", 4);
1941 Target->BufUsed += 4;
1943 else if (*aptr == '&') {
1944 memcpy(bptr, "&", 5);
1946 Target->BufUsed += 5;
1948 else if (*aptr == '"') {
1949 memcpy(bptr, """, 6);
1951 Target->BufUsed += 6;
1953 else if (*aptr == '\'') {
1954 memcpy(bptr, "'", 5);
1956 Target->BufUsed += 5;
1958 else if (*aptr == LB) {
1963 else if (*aptr == RB) {
1968 else if (*aptr == QU) {
1973 else if ((*aptr == 32) && (nbsp == 1)) {
1974 memcpy(bptr, " ", 6);
1976 Target->BufUsed += 6;
1978 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1979 *bptr='\0'; /* nothing */
1981 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1982 memcpy(bptr, "<br/>", 11);
1984 Target->BufUsed += 11;
1988 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1989 *bptr='\0'; /* nothing */
1999 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2001 return Target->BufUsed;
2005 * @ingroup StrBuf_DeEnCoder
2006 * @brief Append a string, escaping characters which have meaning in HTML.
2007 * Converts linebreaks into blanks; escapes single quotes
2008 * @param Target target buffer
2009 * @param Source source buffer; set to NULL if you just have a C-String
2010 * @param PlainIn Plain-C string to append; set to NULL if unused
2012 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2014 const char *aptr, *eiptr;
2018 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2021 if (PlainIn != NULL) {
2023 len = strlen(PlainIn);
2028 eiptr = aptr + Source->BufUsed;
2029 len = Source->BufUsed;
2035 eptr = Target->buf + Target->BufSize - 8;
2036 tptr = Target->buf + Target->BufUsed;
2038 while (aptr < eiptr){
2040 IncreaseBuf(Target, 1, -1);
2041 eptr = Target->buf + Target->BufSize - 8;
2042 tptr = Target->buf + Target->BufUsed;
2045 if (*aptr == '\n') {
2049 else if (*aptr == '\r') {
2053 else if (*aptr == '\'') {
2059 Target->BufUsed += 5;
2072 * @ingroup StrBuf_DeEnCoder
2073 * @brief Append a string, escaping characters which have meaning in ICAL.
2075 * @param Target target buffer
2076 * @param Source source buffer; set to NULL if you just have a C-String
2077 * @param PlainIn Plain-C string to append; set to NULL if unused
2079 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2081 const char *aptr, *eiptr;
2085 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2088 if (PlainIn != NULL) {
2090 len = strlen(PlainIn);
2095 eiptr = aptr + Source->BufUsed;
2096 len = Source->BufUsed;
2102 eptr = Target->buf + Target->BufSize - 8;
2103 tptr = Target->buf + Target->BufUsed;
2105 while (aptr < eiptr){
2106 if(tptr + 3 >= eptr) {
2107 IncreaseBuf(Target, 1, -1);
2108 eptr = Target->buf + Target->BufSize - 8;
2109 tptr = Target->buf + Target->BufUsed;
2112 if (*aptr == '\n') {
2119 else if (*aptr == '\r') {
2126 else if (*aptr == ',') {
2142 * @ingroup StrBuf_DeEnCoder
2143 * @brief Append a string, escaping characters which have meaning in JavaScript strings .
2145 * @param Target target buffer
2146 * @param Source source buffer; set to NULL if you just have a C-String
2147 * @param PlainIn Plain-C string to append; set to NULL if unused
2148 * @returns size of result or -1
2150 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2152 const char *aptr, *eiptr;
2157 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2160 if (PlainIn != NULL) {
2162 len = strlen(PlainIn);
2167 eiptr = aptr + Source->BufUsed;
2168 len = Source->BufUsed;
2174 bptr = Target->buf + Target->BufUsed;
2175 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2177 while (aptr < eiptr){
2179 IncreaseBuf(Target, 1, -1);
2180 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2181 bptr = Target->buf + Target->BufUsed;
2185 memcpy(bptr, HKEY("\\n"));
2187 Target->BufUsed += 2;
2190 memcpy(bptr, HKEY("\\r"));
2192 Target->BufUsed += 2;
2199 Target->BufUsed += 2;
2202 if ((*(aptr + 1) == 'u') &&
2203 isxdigit(*(aptr + 2)) &&
2204 isxdigit(*(aptr + 3)) &&
2205 isxdigit(*(aptr + 4)) &&
2206 isxdigit(*(aptr + 5)))
2207 { /* oh, a unicode escaper. let it pass through. */
2208 memcpy(bptr, aptr, 6);
2211 Target->BufUsed += 6;
2219 Target->BufUsed += 2;
2227 Target->BufUsed += 2;
2234 Target->BufUsed += 2;
2241 Target->BufUsed += 2;
2244 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2245 while (IsUtf8Sequence > 0){
2248 if (--IsUtf8Sequence)
2256 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2258 return Target->BufUsed;
2262 * @ingroup StrBuf_DeEnCoder
2263 * @brief Append a string, escaping characters which have meaning in HTML + json.
2265 * @param Target target buffer
2266 * @param Source source buffer; set to NULL if you just have a C-String
2267 * @param PlainIn Plain-C string to append; set to NULL if unused
2268 * @param nbsp If nonzero, spaces are converted to non-breaking spaces.
2269 * @param nolinebreaks if set to 1, linebreaks are removed from the string.
2270 * if set to 2, linebreaks are replaced by <br/>
2272 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2274 const char *aptr, *eiptr;
2277 int IsUtf8Sequence = 0;
2279 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2282 if (PlainIn != NULL) {
2284 len = strlen(PlainIn);
2289 eiptr = aptr + Source->BufUsed;
2290 len = Source->BufUsed;
2296 bptr = Target->buf + Target->BufUsed;
2297 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2299 while (aptr < eiptr){
2301 IncreaseBuf(Target, 1, -1);
2302 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2303 bptr = Target->buf + Target->BufUsed;
2307 memcpy(bptr, HKEY("<"));
2309 Target->BufUsed += 4;
2312 memcpy(bptr, HKEY(">"));
2314 Target->BufUsed += 4;
2317 memcpy(bptr, HKEY("&"));
2319 Target->BufUsed += 5;
2332 switch (nolinebreaks) {
2334 *bptr='\0'; /* nothing */
2337 memcpy(bptr, HKEY("<br/>"));
2339 Target->BufUsed += 11;
2342 memcpy(bptr, HKEY("\\n"));
2344 Target->BufUsed += 2;
2348 switch (nolinebreaks) {
2351 *bptr='\0'; /* nothing */
2354 memcpy(bptr, HKEY("\\r"));
2356 Target->BufUsed += 2;
2366 Target->BufUsed += 2;
2369 if ((*(aptr + 1) == 'u') &&
2370 isxdigit(*(aptr + 2)) &&
2371 isxdigit(*(aptr + 3)) &&
2372 isxdigit(*(aptr + 4)) &&
2373 isxdigit(*(aptr + 5)))
2374 { /* oh, a unicode escaper. let it pass through. */
2375 memcpy(bptr, aptr, 6);
2378 Target->BufUsed += 6;
2386 Target->BufUsed += 2;
2394 Target->BufUsed += 2;
2401 Target->BufUsed += 2;
2408 Target->BufUsed += 2;
2412 memcpy(bptr, HKEY(" "));
2414 Target->BufUsed += 6;
2418 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2419 while (IsUtf8Sequence > 0){
2422 if (--IsUtf8Sequence)
2430 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2432 return Target->BufUsed;
2436 * @ingroup StrBuf_DeEnCoder
2437 * @brief unhide special chars hidden to the HTML escaper
2438 * @param target buffer to put the unescaped string in
2439 * @param source buffer to unescape
2441 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
2447 FlushStrBuf(target);
2449 if (source == NULL ||target == NULL)
2454 len = source->BufUsed;
2455 for (a = 0; a < len; ++a) {
2456 if (target->BufUsed >= target->BufSize)
2457 IncreaseBuf(target, 1, -1);
2459 if (source->buf[a] == '=') {
2460 hex[0] = source->buf[a + 1];
2461 hex[1] = source->buf[a + 2];
2464 sscanf(hex, "%02x", &b);
2465 target->buf[target->BufUsed] = b;
2466 target->buf[++target->BufUsed] = 0;
2470 target->buf[target->BufUsed] = source->buf[a];
2471 target->buf[++target->BufUsed] = 0;
2478 * @ingroup StrBuf_DeEnCoder
2479 * @brief hide special chars from the HTML escapers and friends
2480 * @param target buffer to put the escaped string in
2481 * @param source buffer to escape
2483 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
2488 FlushStrBuf(target);
2490 if (source == NULL ||target == NULL)
2495 len = source->BufUsed;
2496 for (i=0; i<len; ++i) {
2497 if (target->BufUsed + 4 >= target->BufSize)
2498 IncreaseBuf(target, 1, -1);
2499 if ( (isalnum(source->buf[i])) ||
2500 (source->buf[i]=='-') ||
2501 (source->buf[i]=='_') ) {
2502 target->buf[target->BufUsed++] = source->buf[i];
2505 sprintf(&target->buf[target->BufUsed],
2507 (0xFF &source->buf[i]));
2508 target->BufUsed += 3;
2511 target->buf[target->BufUsed + 1] = '\0';
2515 /*******************************************************************************
2516 * Quoted Printable de/encoding *
2517 *******************************************************************************/
2520 * @ingroup StrBuf_DeEnCoder
2521 * @brief decode a buffer from base 64 encoding; destroys original
2522 * @param Buf Buffor to transform
2524 int StrBufDecodeBase64(StrBuf *Buf)
2528 if (Buf == NULL) return -1;
2530 xferbuf = (char*) malloc(Buf->BufSize);
2532 siz = CtdlDecodeBase64(xferbuf,
2542 * @ingroup StrBuf_DeEnCoder
2543 * @brief decode a buffer from base 64 encoding; destroys original
2544 * @param Buf Buffor to transform
2546 int StrBufDecodeHex(StrBuf *Buf)
2549 char *pch, *pche, *pchi;
2551 if (Buf == NULL) return -1;
2553 pch = pchi = Buf->buf;
2554 pche = pch + Buf->BufUsed;
2556 while (pchi < pche){
2557 ch = decode_hex(pchi);
2564 Buf->BufUsed = pch - Buf->buf;
2565 return Buf->BufUsed;
2569 * @ingroup StrBuf_DeEnCoder
2570 * @brief replace all chars >0x20 && < 0x7F with Mute
2571 * @param Mute char to put over invalid chars
2572 * @param Buf Buffor to transform
2574 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2578 if (Buf == NULL) return -1;
2579 pch = (unsigned char *)Buf->buf;
2580 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2581 if ((*pch < 0x20) || (*pch > 0x7F))
2585 return Buf->BufUsed;
2590 * @ingroup StrBuf_DeEnCoder
2591 * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
2592 * @param Buf Buffer to translate
2593 * @param StripBlanks Reduce several blanks to one?
2595 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2604 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2605 Buf->buf[Buf->BufUsed - 1] = '\0';
2610 while (a < Buf->BufUsed) {
2611 if (Buf->buf[a] == '+')
2613 else if (Buf->buf[a] == '%') {
2614 /* don't let % chars through, rather truncate the input. */
2615 if (a + 2 > Buf->BufUsed) {
2620 hex[0] = Buf->buf[a + 1];
2621 hex[1] = Buf->buf[a + 2];
2624 sscanf(hex, "%02x", &b);
2625 Buf->buf[a] = (char) b;
2626 len = Buf->BufUsed - a - 2;
2628 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2640 * @ingroup StrBuf_DeEnCoder
2641 * @brief RFC2047-encode a header field if necessary.
2642 * If no non-ASCII characters are found, the string
2643 * will be copied verbatim without encoding.
2645 * @param target Target buffer.
2646 * @param source Source string to be encoded.
2647 * @returns encoded length; -1 if non success.
2649 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2651 const char headerStr[] = "=?UTF-8?Q?";
2652 int need_to_encode = 0;
2656 if ((source == NULL) ||
2660 while ((i < source->BufUsed) &&
2661 (!IsEmptyStr (&source->buf[i])) &&
2662 (need_to_encode == 0)) {
2663 if (((unsigned char) source->buf[i] < 32) ||
2664 ((unsigned char) source->buf[i] > 126)) {
2670 if (!need_to_encode) {
2671 if (*target == NULL) {
2672 *target = NewStrBufPlain(source->buf, source->BufUsed);
2675 FlushStrBuf(*target);
2676 StrBufAppendBuf(*target, source, 0);
2678 return (*target)->BufUsed;
2680 if (*target == NULL)
2681 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2682 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2683 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2684 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2685 (*target)->BufUsed = sizeof(headerStr) - 1;
2686 for (i=0; (i < source->BufUsed); ++i) {
2687 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2688 IncreaseBuf(*target, 1, 0);
2689 ch = (unsigned char) source->buf[i];
2699 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2700 (*target)->BufUsed += 3;
2704 (*target)->buf[(*target)->BufUsed] = '_';
2706 (*target)->buf[(*target)->BufUsed] = ch;
2707 (*target)->BufUsed++;
2711 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2712 IncreaseBuf(*target, 1, 0);
2714 (*target)->buf[(*target)->BufUsed++] = '?';
2715 (*target)->buf[(*target)->BufUsed++] = '=';
2716 (*target)->buf[(*target)->BufUsed] = '\0';
2717 return (*target)->BufUsed;;
2722 static void AddRecipient(StrBuf *Target,
2724 StrBuf *EmailAddress,
2729 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
2730 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
2732 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
2733 StrBufRFC2047encode(&EncBuf, UserName);
2734 StrBufAppendBuf(Target, EncBuf, 0);
2735 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
2736 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
2738 if (StrLength(EmailAddress) > 0){
2739 StrBufAppendBufPlain(Target, HKEY("<"), 0);
2740 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
2741 StrBufAppendBufPlain(Target, HKEY(">"), 0);
2747 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
2748 * \param Recp Source list of email recipients
2749 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
2750 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
2751 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
2752 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
2754 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
2756 StrBuf *EmailAddress,
2760 const char *pch, *pche;
2761 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
2763 if ((Recp == NULL) || (StrLength(Recp) == 0))
2767 pche = pch + StrLength(Recp);
2769 if (!CheckEncode(pch, -1, pche))
2770 return NewStrBufDup(Recp);
2772 Target = NewStrBufPlain(NULL, StrLength(Recp));
2774 while ((pch != NULL) && (pch < pche))
2776 while (isspace(*pch)) pch++;
2777 UserStart = UserEnd = EmailStart = EmailEnd = NULL;
2779 if ((*pch == '"') || (*pch == '\'')) {
2780 UserStart = pch + 1;
2782 UserEnd = strchr(UserStart, *pch);
2783 if (UserEnd == NULL)
2784 break; ///TODO: Userfeedback??
2785 EmailStart = UserEnd + 1;
2786 while (isspace(*EmailStart))
2788 if (UserEnd == UserStart) {
2789 UserStart = UserEnd = NULL;
2792 if (*EmailStart == '<') {
2794 EmailEnd = strchr(EmailStart, '>');
2795 if (EmailEnd == NULL)
2796 EmailEnd = strchr(EmailStart, ',');
2800 EmailEnd = strchr(EmailStart, ',');
2802 if (EmailEnd == NULL)
2809 EmailEnd = strchr(UserStart, ',');
2810 if (EmailEnd == NULL) {
2811 EmailEnd = strchr(pch, '>');
2813 if (EmailEnd != NULL) {
2823 while ((EmailEnd > UserStart) && !gt &&
2824 ((*EmailEnd == ',') ||
2825 (*EmailEnd == '>') ||
2826 (isspace(*EmailEnd))))
2828 if (*EmailEnd == '>')
2833 if (EmailEnd == UserStart)
2837 EmailStart = strchr(UserStart, '<');
2838 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
2840 UserEnd = EmailStart;
2842 while ((UserEnd > UserStart) &&
2843 isspace (*(UserEnd - 1)))
2846 if (UserStart >= UserEnd)
2847 UserStart = UserEnd = NULL;
2848 At = strchr(EmailStart, '@');
2850 else { /* this is a local recipient... no domain, just a realname */
2851 EmailStart = UserStart;
2852 At = strchr(EmailStart, '@');
2858 EmailStart = UserStart;
2864 if ((UserStart != NULL) && (UserEnd != NULL))
2865 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2866 else if ((UserStart != NULL) && (UserEnd == NULL))
2867 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
2869 FlushStrBuf(UserName);
2871 if ((EmailStart != NULL) && (EmailEnd != NULL))
2872 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
2873 else if ((EmailStart != NULL) && (EmailEnd == NULL))
2874 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
2876 FlushStrBuf(EmailAddress);
2878 AddRecipient(Target, UserName, EmailAddress, EncBuf);
2883 if ((pch != NULL) && (*pch == ','))
2885 if (pch != NULL) while (isspace(*pch))
2894 * @brief replaces all occurances of 'search' by 'replace'
2895 * @param buf Buffer to modify
2896 * @param search character to search
2897 * @param replace character to replace search by
2899 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
2904 for (i=0; i<buf->BufUsed; i++)
2905 if (buf->buf[i] == search)
2906 buf->buf[i] = replace;
2912 * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
2913 * @param buf Buffer to modify
2915 void StrBufToUnixLF(StrBuf *buf)
2917 char *pche, *pchS, *pchT;
2921 pche = buf->buf + buf->BufUsed;
2922 pchS = pchT = buf->buf;
2928 if (*pchS != '\n') {
2937 buf->BufUsed = pchT - buf->buf;
2941 /*******************************************************************************
2942 * Iconv Wrapper; RFC822 de/encoding *
2943 *******************************************************************************/
2946 * @ingroup StrBuf_DeEnCoder
2947 * @brief Wrapper around iconv_open()
2948 * Our version adds aliases for non-standard Microsoft charsets
2949 * such as 'MS950', aliasing them to names like 'CP950'
2951 * @param tocode Target encoding
2952 * @param fromcode Source encoding
2953 * @param pic anonimized pointer to iconv struct
2955 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
2958 iconv_t ic = (iconv_t)(-1) ;
2959 ic = iconv_open(tocode, fromcode);
2960 if (ic == (iconv_t)(-1) ) {
2961 char alias_fromcode[64];
2962 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
2963 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
2964 alias_fromcode[0] = 'C';
2965 alias_fromcode[1] = 'P';
2966 ic = iconv_open(tocode, alias_fromcode);
2969 *(iconv_t *)pic = ic;
2975 * @ingroup StrBuf_DeEnCoder
2976 * @brief find one chunk of a RFC822 encoded string
2977 * @param Buffer where to search
2978 * @param bptr where to start searching
2979 * @returns found position, NULL if none.
2981 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
2984 /* Find the next ?Q? */
2985 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
2988 end = strchr(bptr + 2, '?');
2993 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
2994 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
2995 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
2996 (*(end + 2) == '?')) {
2997 /* skip on to the end of the cluster, the next ?= */
2998 end = strstr(end + 3, "?=");
3001 /* sort of half valid encoding, try to find an end. */
3002 end = strstr(bptr, "?=");
3009 * @ingroup StrBuf_DeEnCoder
3010 * @brief convert one buffer according to the preselected iconv pointer PIC
3011 * @param ConvertBuf buffer we need to translate
3012 * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3013 * @param pic Pointer to the iconv-session Object
3015 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3021 char *ibuf; /**< Buffer of characters to be converted */
3022 char *obuf; /**< Buffer for converted characters */
3023 size_t ibuflen; /**< Length of input buffer */
3024 size_t obuflen; /**< Length of output buffer */
3027 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3028 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3029 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3031 ic = *(iconv_t*)pic;
3032 ibuf = ConvertBuf->buf;
3033 ibuflen = ConvertBuf->BufUsed;
3035 obuflen = TmpBuf->BufSize;
3037 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3040 if (errno == E2BIG) {
3042 IncreaseBuf(TmpBuf, 0, 0);
3047 else if (errno == EILSEQ){
3048 /* hm, invalid utf8 sequence... what to do now? */
3049 /* An invalid multibyte sequence has been encountered in the input */
3051 else if (errno == EINVAL) {
3052 /* An incomplete multibyte sequence has been encountered in the input. */
3055 FlushStrBuf(TmpBuf);
3058 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3059 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3061 /* little card game: wheres the red lady? */
3062 SwapBuffers(ConvertBuf, TmpBuf);
3063 FlushStrBuf(TmpBuf);
3070 * @ingroup StrBuf_DeEnCoder
3071 * @brief catches one RFC822 encoded segment, and decodes it.
3072 * @param Target buffer to fill with result
3073 * @param DecodeMe buffer with stuff to process
3074 * @param SegmentStart points to our current segment in DecodeMe
3075 * @param SegmentEnd Points to the end of our current segment in DecodeMe
3076 * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3077 * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3078 * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3080 inline static void DecodeSegment(StrBuf *Target,
3081 const StrBuf *DecodeMe,
3082 const char *SegmentStart,
3083 const char *SegmentEnd,
3085 StrBuf *ConvertBuf2,
3086 StrBuf *FoundCharset)
3092 iconv_t ic = (iconv_t)(-1);
3096 /* Now we handle foreign character sets properly encoded
3097 * in RFC2047 format.
3099 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3100 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3101 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3102 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3103 if (FoundCharset != NULL) {
3104 FlushStrBuf(FoundCharset);
3105 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3107 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3108 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3110 *encoding = toupper(*encoding);
3111 if (*encoding == 'B') { /**< base64 */
3112 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3113 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3114 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3116 ConvertBuf->BufUsed);
3118 else if (*encoding == 'Q') { /**< quoted-printable */
3122 while (pos < ConvertBuf->BufUsed)
3124 if (ConvertBuf->buf[pos] == '_')
3125 ConvertBuf->buf[pos] = ' ';
3129 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3130 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3132 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3135 ConvertBuf->BufUsed);
3138 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3141 ctdl_iconv_open("UTF-8", charset, &ic);
3142 if (ic != (iconv_t)(-1) ) {
3144 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3145 StrBufAppendBuf(Target, ConvertBuf2, 0);
3150 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3156 * @ingroup StrBuf_DeEnCoder
3157 * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3158 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3159 * @param Target where to put the decoded string to
3160 * @param DecodeMe buffer with encoded string
3161 * @param DefaultCharset if we don't find one, which should we use?
3162 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3163 * put it here for later use where no string might be known.
3165 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3168 StrBuf *ConvertBuf2;
3169 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3170 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3172 StrBuf_RFC822_2_Utf8(Target,
3178 FreeStrBuf(&ConvertBuf);
3179 FreeStrBuf(&ConvertBuf2);
3183 * @ingroup StrBuf_DeEnCoder
3184 * @brief Handle subjects with RFC2047 encoding such as:
3185 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3186 * @param Target where to put the decoded string to
3187 * @param DecodeMe buffer with encoded string
3188 * @param DefaultCharset if we don't find one, which should we use?
3189 * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3190 * put it here for later use where no string might be known.
3191 * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3192 * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3194 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3195 const StrBuf *DecodeMe,
3196 const StrBuf* DefaultCharset,
3197 StrBuf *FoundCharset,
3199 StrBuf *ConvertBuf2)
3201 StrBuf *DecodedInvalidBuf = NULL;
3202 const StrBuf *DecodeMee = DecodeMe;
3203 const char *start, *end, *next, *nextend, *ptr = NULL;
3205 iconv_t ic = (iconv_t)(-1) ;
3210 int illegal_non_rfc2047_encoding = 0;
3212 /* Sometimes, badly formed messages contain strings which were simply
3213 * written out directly in some foreign character set instead of
3214 * using RFC2047 encoding. This is illegal but we will attempt to
3215 * handle it anyway by converting from a user-specified default
3216 * charset to UTF-8 if we see any nonprintable characters.
3219 len = StrLength(DecodeMe);
3220 for (i=0; i<DecodeMe->BufUsed; ++i) {
3221 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3222 illegal_non_rfc2047_encoding = 1;
3227 if ((illegal_non_rfc2047_encoding) &&
3228 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3229 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3232 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3233 if (ic != (iconv_t)(-1) ) {
3234 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3235 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3236 DecodeMee = DecodedInvalidBuf;
3242 /* pre evaluate the first pair */
3243 nextend = end = NULL;
3244 len = StrLength(DecodeMee);
3245 start = strstr(DecodeMee->buf, "=?");
3246 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3248 end = FindNextEnd (DecodeMee, start + 2);
3250 StrBufAppendBuf(Target, DecodeMee, 0);
3251 FreeStrBuf(&DecodedInvalidBuf);
3256 if (start != DecodeMee->buf) {
3259 nFront = start - DecodeMee->buf;
3260 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3264 * Since spammers will go to all sorts of absurd lengths to get their
3265 * messages through, there are LOTS of corrupt headers out there.
3266 * So, prevent a really badly formed RFC2047 header from throwing
3267 * this function into an infinite loop.
3269 while ((start != NULL) &&
3276 DecodeSegment(Target,
3284 next = strstr(end, "=?");
3286 if ((next != NULL) &&
3288 nextend = FindNextEnd(DecodeMee, next);
3289 if (nextend == NULL)
3292 /* did we find two partitions */
3293 if ((next != NULL) &&
3297 while ((ptr < next) &&
3304 * did we find a gab just filled with blanks?
3305 * if not, copy its stuff over.
3309 StrBufAppendBufPlain(Target,
3315 /* our next-pair is our new first pair now. */
3321 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3322 if ((end != NULL) && (end < nextend)) {
3324 while ( (ptr < nextend) &&
3331 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3333 FreeStrBuf(&DecodedInvalidBuf);
3336 /*******************************************************************************
3337 * Manipulating UTF-8 Strings *
3338 *******************************************************************************/
3342 * @brief evaluate the length of an utf8 special character sequence
3343 * @param Char the character to examine
3344 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3346 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3349 unsigned char test = (1<<7);
3351 if ((*CharS & 0xC0) != 0xC0)
3355 ((test & ((unsigned char)*CharS)) != 0))
3360 if ((n > 6) || ((CharE - CharS) < n))
3367 * @brief detect whether this char starts an utf-8 encoded char
3368 * @param Char character to inspect
3369 * @returns yes or no
3371 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3373 /** 11??.???? indicates an UTF8 Sequence. */
3374 return ((Char & 0xC0) == 0xC0);
3379 * @brief measure the number of glyphs in an UTF8 string...
3380 * @param Buf string to measure
3381 * @returns the number of glyphs in Buf
3383 long StrBuf_Utf8StrLen(StrBuf *Buf)
3389 if ((Buf == NULL) || (Buf->BufUsed == 0))
3392 eptr = Buf->buf + Buf->BufUsed;
3393 while ((aptr < eptr) && (*aptr != '\0')) {
3394 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3395 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3396 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3409 * @brief cuts a string after maxlen glyphs
3410 * @param Buf string to cut to maxlen glyphs
3411 * @param maxlen how long may the string become?
3412 * @returns current length of the string
3414 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3420 eptr = Buf->buf + Buf->BufUsed;
3421 while ((aptr < eptr) && (*aptr != '\0')) {
3422 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3423 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3424 while ((*aptr++ != '\0') && (m-- > 0));
3433 Buf->BufUsed = aptr - Buf->buf;
3434 return Buf->BufUsed;
3437 return Buf->BufUsed;
3445 /*******************************************************************************
3447 *******************************************************************************/
3450 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
3451 #define OS_CODE 0x03 /*< unix */
3454 * @ingroup StrBuf_DeEnCoder
3455 * @brief uses the same calling syntax as compress2(), but it
3456 * creates a stream compatible with HTTP "Content-encoding: gzip"
3457 * @param dest compressed buffer
3458 * @param destLen length of the compresed data
3459 * @param source source to encode
3460 * @param sourceLen length of source to encode
3461 * @param level compression level
3463 int ZEXPORT compress_gzip(Bytef * dest,
3465 const Bytef * source,
3469 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
3471 /* write gzip header */
3472 snprintf((char *) dest, *destLen,
3473 "%c%c%c%c%c%c%c%c%c%c",
3474 gz_magic[0], gz_magic[1], Z_DEFLATED,
3475 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3478 /* normal deflate */
3481 stream.next_in = (Bytef *) source;
3482 stream.avail_in = (uInt) sourceLen;
3483 stream.next_out = dest + 10L; // after header
3484 stream.avail_out = (uInt) * destLen;
3485 if ((uLong) stream.avail_out != *destLen)
3488 stream.zalloc = (alloc_func) 0;
3489 stream.zfree = (free_func) 0;
3490 stream.opaque = (voidpf) 0;
3492 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3493 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3497 err = deflate(&stream, Z_FINISH);
3498 if (err != Z_STREAM_END) {
3499 deflateEnd(&stream);
3500 return err == Z_OK ? Z_BUF_ERROR : err;
3502 *destLen = stream.total_out + 10L;
3504 /* write CRC and Length */
3505 uLong crc = crc32(0L, source, sourceLen);
3507 for (n = 0; n < 4; ++n, ++*destLen) {
3508 dest[*destLen] = (int) (crc & 0xff);
3511 uLong len = stream.total_in;
3512 for (n = 0; n < 4; ++n, ++*destLen) {
3513 dest[*destLen] = (int) (len & 0xff);
3516 err = deflateEnd(&stream);
3523 * @ingroup StrBuf_DeEnCoder
3524 * @brief compress the buffer with gzip
3525 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3526 * @param Buf buffer whose content is to be gzipped
3528 int CompressBuffer(StrBuf *Buf)
3531 char *compressed_data = NULL;
3532 size_t compressed_len, bufsize;
3535 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3536 compressed_data = malloc(compressed_len);
3538 if (compressed_data == NULL)
3540 /* Flush some space after the used payload so valgrind shuts up... */
3541 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3542 Buf->buf[Buf->BufUsed + i++] = '\0';
3543 if (compress_gzip((Bytef *) compressed_data,
3546 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3549 Buf->buf = compressed_data;
3550 Buf->BufUsed = compressed_len;
3551 Buf->BufSize = bufsize;
3552 /* Flush some space after the used payload so valgrind shuts up... */
3554 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3555 Buf->buf[Buf->BufUsed + i++] = '\0';
3558 free(compressed_data);
3560 #endif /* HAVE_ZLIB */
3564 /*******************************************************************************
3565 * File I/O; Callbacks to libevent *
3566 *******************************************************************************/
3568 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3573 if ((FB == NULL) || (FB->Buf == NULL))
3577 * check whether the read pointer is somewhere in a range
3578 * where a cut left is inexpensive
3581 if (FB->ReadWritePointer != NULL)
3585 already_read = FB->ReadWritePointer - FB->Buf->buf;
3586 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3588 if (already_read != 0) {
3591 unread = FB->Buf->BufUsed - already_read;
3593 /* else nothing to compact... */
3595 FB->ReadWritePointer = FB->Buf->buf;
3596 bufremain = FB->Buf->BufSize;
3598 else if ((unread < 64) ||
3599 (bufremain < already_read))
3602 * if its just a tiny bit remaining, or we run out of space...
3605 FB->Buf->BufUsed = unread;
3606 if (unread < already_read)
3607 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3609 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3610 FB->ReadWritePointer = FB->Buf->buf;
3611 bufremain = FB->Buf->BufSize - unread - 1;
3613 else if (bufremain < (FB->Buf->BufSize / 10))
3615 /* get a bigger buffer */
3617 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3619 FB->ReadWritePointer = FB->Buf->buf + unread;
3621 bufremain = FB->Buf->BufSize - unread - 1;
3622 /*TODO: special increase function that won't copy the already read! */
3625 else if (bufremain < 10) {
3626 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3628 FB->ReadWritePointer = FB->Buf->buf;
3630 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3635 FB->ReadWritePointer = FB->Buf->buf;
3636 bufremain = FB->Buf->BufSize - 1;
3639 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3642 FB->Buf->BufUsed += n;
3643 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3648 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
3653 if ((FB == NULL) || (FB->Buf == NULL))
3656 if (FB->ReadWritePointer != NULL)
3658 WriteRemain = FB->Buf->BufUsed -
3659 (FB->ReadWritePointer -
3663 FB->ReadWritePointer = FB->Buf->buf;
3664 WriteRemain = FB->Buf->BufUsed;
3667 n = write(fd, FB->ReadWritePointer, WriteRemain);
3669 FB->ReadWritePointer += n;
3671 if (FB->ReadWritePointer ==
3672 FB->Buf->buf + FB->Buf->BufUsed)
3674 FlushStrBuf(FB->Buf);
3675 FB->ReadWritePointer = NULL;
3678 // check whether we've got something to write
3679 // get the maximum chunk plus the pointer we can send
3680 // write whats there
3681 // if not all was sent, remember the send pointer for the next time
3682 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3688 * @ingroup StrBuf_IO
3689 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
3690 * @param LineBuf your line will be copied here.
3691 * @param FB BLOB with lines of text...
3692 * @param Ptr moved arround to keep the next-line across several iterations
3693 * has to be &NULL on start; will be &NotNULL on end of buffer
3694 * @returns size of copied buffer
3696 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
3698 const char *aptr, *ptr, *eptr;
3701 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
3702 FB->ReadWritePointer = StrBufNOTNULL;
3706 FlushStrBuf(LineBuf);
3707 if (FB->ReadWritePointer == NULL)
3708 ptr = aptr = FB->Buf->buf;
3710 ptr = aptr = FB->ReadWritePointer;
3712 optr = LineBuf->buf;
3713 eptr = FB->Buf->buf + FB->Buf->BufUsed;
3714 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3716 while ((ptr <= eptr) &&
3723 LineBuf->BufUsed = optr - LineBuf->buf;
3724 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
3725 optr = LineBuf->buf + LineBuf->BufUsed;
3726 xptr = LineBuf->buf + LineBuf->BufSize - 1;
3731 if (optr > LineBuf->buf)
3733 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
3734 LineBuf->BufUsed = optr - LineBuf->buf;
3736 if ((FB->ReadWritePointer != NULL) &&
3737 (FB->ReadWritePointer != FB->Buf->buf))
3739 /* Ok, the client application read all the data
3740 it was interested in so far. Since there is more to read,
3741 we now shrink the buffer, and move the rest over.
3743 StrBufCutLeft(FB->Buf,
3744 FB->ReadWritePointer - FB->Buf->buf);
3745 FB->ReadWritePointer = FB->Buf->buf;
3747 return eMustReadMore;
3750 LineBuf->BufUsed = optr - LineBuf->buf;
3752 if ((ptr <= eptr) && (*ptr == '\r'))
3754 if ((ptr <= eptr) && (*ptr == '\n'))
3758 FB->ReadWritePointer = ptr;
3761 FlushStrBuf(FB->Buf);
3762 FB->ReadWritePointer = NULL;
3765 return eReadSuccess;
3769 * @ingroup StrBuf_CHUNKED_IO
3770 * @brief check whether the chunk-buffer has more data waiting or not.
3771 * @param FB Chunk-Buffer to inspect
3773 eReadState StrBufCheckBuffer(IOBuffer *FB)
3777 if (FB->Buf->BufUsed == 0)
3778 return eReadSuccess;
3779 if (FB->ReadWritePointer == NULL)
3780 return eBufferNotEmpty;
3781 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
3782 return eBufferNotEmpty;
3783 return eReadSuccess;
3786 long IOBufferStrLength(IOBuffer *FB)
3788 if (FB->ReadWritePointer == NULL)
3789 return StrLength(FB->Buf);
3791 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
3796 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
3798 memset(FDB, 0, sizeof(FDIOBuffer));
3800 FDB->TotalSendSize = TotalSendSize;
3802 #ifndef LINUX_SENDFILE
3803 FDB->ChunkBuffer = NewStrBuf();
3805 pipe(FDB->SplicePipe);
3810 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
3813 #ifdef LINUX_SENDFILE
3815 sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
3818 *Err = strerror(errno);
3821 FDB->ChunkSendRemain -= sent;
3822 return FDB->ChunkSendRemain;
3828 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
3831 #ifdef LINUX_SENDFILE
3832 ssize_t sent, pipesize;
3835 pipesize = splice(FDB->IOB->fd, NULL,
3836 FDB->SplicePipe[1], NULL,
3837 FDB->ChunkSendRemain,
3838 SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
3841 *Err = strerror(errno);
3845 sent = splice(FDB->SplicePipe[0], NULL,
3846 FDB->OtherFD, &FDB->TotalSentAlready,
3847 pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
3850 *Err = strerror(errno);
3853 FDB->ChunkSendRemain -= sent;
3860 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
3866 int nSuccessLess = 0;
3870 fdflags = fcntl(FDB->OtherFD, F_GETFL);
3871 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
3873 while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) &&
3874 (FDB->ChunkSendRemain > 0))
3877 tv.tv_sec = 1; /* selectresolution; */
3881 FD_SET(FDB->OtherFD, &rfds);
3882 if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) {
3883 *Error = strerror(errno);
3887 if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) {
3892 should_write = FDB->IOB->Buf->BufUsed -
3893 (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf);
3894 if (should_write > FDB->ChunkSendRemain)
3895 should_write = FDB->ChunkSendRemain;
3897 rlen = write(FDB->OtherFD,
3898 FDB->IOB->ReadWritePointer,
3901 *Error = strerror(errno);
3905 FDB->TotalSentAlready += rlen;
3906 FDB->IOB->ReadWritePointer += rlen;
3907 FDB->ChunkSendRemain -= rlen;
3909 if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed)
3911 FlushStrBuf(FDB->IOB->Buf);
3912 FDB->IOB->ReadWritePointer = NULL;
3915 if (FDB->ChunkSendRemain == 0)
3916 return eReadSuccess;
3918 return eMustReadMore;
3921 /*******************************************************************************
3922 * File I/O; Prefer buffered read since its faster! *
3923 *******************************************************************************/
3926 * @ingroup StrBuf_IO
3927 * @brief Read a line from socket
3928 * flushes and closes the FD on error
3929 * @param buf the buffer to get the input to
3930 * @param fd pointer to the filedescriptor to read
3931 * @param append Append to an existing string or replace?
3932 * @param Error strerror() on error
3933 * @returns numbers of chars read
3935 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
3937 int len, rlen, slen;
3942 slen = len = buf->BufUsed;
3944 rlen = read(*fd, &buf->buf[len], 1);
3946 *Error = strerror(errno);
3953 if (buf->buf[len] == '\n')
3955 if (buf->buf[len] != '\r')
3957 if (len + 2 >= buf->BufSize) {
3959 buf->buf[len+1] = '\0';
3960 IncreaseBuf(buf, 1, -1);
3964 buf->buf[len] = '\0';
3969 * @ingroup StrBuf_BufferedIO
3970 * @brief Read a line from socket
3971 * flushes and closes the FD on error
3972 * @param Line the line to read from the fd / I/O Buffer
3973 * @param buf the buffer to get the input to
3974 * @param fd pointer to the filedescriptor to read
3975 * @param timeout number of successless selects until we bail out
3976 * @param selectresolution how long to wait on each select
3977 * @param Error strerror() on error
3978 * @returns numbers of chars read
3980 int StrBufTCP_read_buffered_line(StrBuf *Line,
3984 int selectresolution,
3988 int nSuccessLess = 0;
3995 if (buf->BufUsed > 0) {
3996 pch = strchr(buf->buf, '\n');
3999 len = pch - buf->buf;
4000 if (len > 0 && (*(pch - 1) == '\r') )
4002 StrBufSub(Line, buf, 0, len - rlen);
4003 StrBufCutLeft(buf, len + 1);
4008 if (buf->BufSize - buf->BufUsed < 10)
4009 IncreaseBuf(buf, 1, -1);
4011 fdflags = fcntl(*fd, F_GETFL);
4012 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4014 while ((nSuccessLess < timeout) && (pch == NULL)) {
4016 tv.tv_sec = selectresolution;
4021 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4022 *Error = strerror(errno);
4028 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4033 &buf->buf[buf->BufUsed],
4034 buf->BufSize - buf->BufUsed - 1);
4036 *Error = strerror(errno);
4041 else if (rlen > 0) {
4043 buf->BufUsed += rlen;
4044 buf->buf[buf->BufUsed] = '\0';
4045 if (buf->BufUsed + 10 > buf->BufSize) {
4046 IncreaseBuf(buf, 1, -1);
4048 pch = strchr(buf->buf, '\n');
4055 len = pch - buf->buf;
4056 if (len > 0 && (*(pch - 1) == '\r') )
4058 StrBufSub(Line, buf, 0, len - rlen);
4059 StrBufCutLeft(buf, len + 1);
4066 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4067 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4068 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4070 * @ingroup StrBuf_BufferedIO
4071 * @brief Read a line from socket
4072 * flushes and closes the FD on error
4073 * @param Line where to append our Line read from the fd / I/O Buffer;
4074 * @param IOBuf the buffer to get the input to; lifetime pair to FD
4075 * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4076 * @param fd pointer to the filedescriptor to read
4077 * @param timeout number of successless selects until we bail out
4078 * @param selectresolution how long to wait on each select
4079 * @param Error strerror() on error
4080 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4082 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4087 int selectresolution,
4090 const char *pche = NULL;
4091 const char *pos = NULL;
4093 int len, rlen, retlen;
4094 int nSuccessLess = 0;
4096 const char *pch = NULL;
4102 if ((Line == NULL) ||
4109 *Error = ErrRBLF_PreConditionFailed;
4114 if ((IOBuf->BufUsed > 0) &&
4116 (pos < IOBuf->buf + IOBuf->BufUsed))
4120 pche = IOBuf->buf + IOBuf->BufUsed;
4124 while ((pch < pche) && (*pch != '\n'))
4126 if (Line->BufUsed + 10 > Line->BufSize)
4129 apos = pcht - Line->buf;
4131 IncreaseBuf(Line, 1, -1);
4132 pcht = Line->buf + apos;
4140 if (len > 0 && (*(pch - 1) == '\r') )
4149 if ((pch >= pche) || (*pch == '\0'))
4157 if ((pch != NULL) &&
4160 if (pch + 1 >= pche) {
4173 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4175 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4176 IncreaseBuf(IOBuf, 1, -1);
4178 fdflags = fcntl(*fd, F_GETFL);
4179 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4182 while ((nSuccessLess < timeout) &&
4192 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4193 *Error = strerror(errno);
4197 *Error = ErrRBLF_SelectFailed;
4200 if (! FD_ISSET(*fd, &rfds) != 0) {
4206 &IOBuf->buf[IOBuf->BufUsed],
4207 IOBuf->BufSize - IOBuf->BufUsed - 1);
4209 *Error = strerror(errno);
4214 else if (rlen > 0) {
4216 pLF = IOBuf->buf + IOBuf->BufUsed;
4217 IOBuf->BufUsed += rlen;
4218 IOBuf->buf[IOBuf->BufUsed] = '\0';
4220 pche = IOBuf->buf + IOBuf->BufUsed;
4222 while ((pLF < pche) && (*pLF != '\n'))
4224 if ((pLF >= pche) || (*pLF == '\0'))
4227 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4231 if (pLF != NULL) apos = pLF - IOBuf->buf;
4232 IncreaseBuf(IOBuf, 1, -1);
4233 if (pLF != NULL) pLF = IOBuf->buf + apos;
4243 if (len > 0 && (*(pLF - 1) == '\r') )
4245 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4246 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4252 return retlen + len;
4254 *Error = ErrRBLF_NotEnoughSentFromServer;
4259 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4261 * @ingroup StrBuf_IO
4262 * @brief Input binary data from socket
4263 * flushes and closes the FD on error
4264 * @param Buf the buffer to get the input to
4265 * @param fd pointer to the filedescriptor to read
4266 * @param append Append to an existing string or replace?
4267 * @param nBytes the maximal number of bytes to read
4268 * @param Error strerror() on error
4269 * @returns numbers of chars read
4271 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4282 if ((Buf == NULL) || (*fd == -1))
4284 *Error = ErrRBLF_BLOBPreConditionFailed;
4289 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4290 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4292 ptr = Buf->buf + Buf->BufUsed;
4294 fdflags = fcntl(*fd, F_GETFL);
4295 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4297 while ((nRead < nBytes) &&
4307 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4308 *Error = strerror(errno);
4312 *Error = ErrRBLF_SelectFailed;
4315 if (! FD_ISSET(*fd, &rfds) != 0) {
4321 if ((rlen = read(*fd,
4323 nBytes - nRead)) == -1) {
4326 *Error = strerror(errno);
4331 Buf->BufUsed += rlen;
4333 Buf->buf[Buf->BufUsed] = '\0';
4337 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4338 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4340 * @ingroup StrBuf_BufferedIO
4341 * @brief Input binary data from socket
4342 * flushes and closes the FD on error
4343 * @param Blob put binary thing here
4344 * @param IOBuf the buffer to get the input to
4345 * @param Pos offset inside of IOBuf
4346 * @param fd pointer to the filedescriptor to read
4347 * @param append Append to an existing string or replace?
4348 * @param nBytes the maximal number of bytes to read
4349 * @param check whether we should search for '000\n' terminators in case of timeouts
4350 * @param Error strerror() on error
4351 * @returns numbers of chars read
4353 int StrBufReadBLOBBuffered(StrBuf *Blob,
4367 int nAlreadyRead = 0;
4372 int nSuccessLess = 0;
4375 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL))
4379 *Error = ErrRBB_BLOBFPreConditionFailed;
4385 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4386 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4391 len = pos - IOBuf->buf;
4392 rlen = IOBuf->BufUsed - len;
4395 if ((IOBuf->BufUsed > 0) &&
4397 (pos < IOBuf->buf + IOBuf->BufUsed))
4399 if (rlen < nBytes) {
4400 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4401 Blob->BufUsed += rlen;
4402 Blob->buf[Blob->BufUsed] = '\0';
4403 nAlreadyRead = nRead = rlen;
4406 if (rlen >= nBytes) {
4407 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4408 Blob->BufUsed += nBytes;
4409 Blob->buf[Blob->BufUsed] = '\0';
4410 if (rlen == nBytes) {
4422 if (IOBuf->BufSize < nBytes - nRead)
4423 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4426 len = Blob->BufUsed;
4428 fdflags = fcntl(*fd, F_GETFL);
4429 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4437 while ((nSuccessLess < MaxTries) &&
4447 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4448 *Error = strerror(errno);
4452 *Error = ErrRBLF_SelectFailed;
4455 if (! FD_ISSET(*fd, &rfds) != 0) {
4462 IOBuf->BufSize - (ptr - IOBuf->buf));
4466 *Error = strerror(errno);
4469 else if (rlen == 0){
4470 if ((check == NNN_TERM) &&
4472 (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0))
4474 StrBufPlain(Blob, HKEY("\n000\n"));
4475 StrBufCutRight(Blob, 5);
4476 return Blob->BufUsed;
4478 else if (!IsNonBlock)
4480 else if (nSuccessLess > MaxTries) {
4482 *Error = ErrRBB_too_many_selects;
4486 else if (rlen > 0) {
4490 IOBuf->BufUsed += rlen;
4493 if (nSuccessLess >= MaxTries) {
4495 *Error = ErrRBB_too_many_selects;
4499 if (nRead > nBytes) {
4500 *Pos = IOBuf->buf + nBytes;
4502 Blob->buf[Blob->BufUsed] = '\0';
4503 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4507 return nRead + nAlreadyRead;
4511 * @ingroup StrBuf_IO
4512 * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4513 * @param LineBuf your line will be copied here.
4514 * @param Buf BLOB with lines of text...
4515 * @param Ptr moved arround to keep the next-line across several iterations
4516 * has to be &NULL on start; will be &NotNULL on end of buffer
4517 * @returns size of remaining buffer
4519 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4521 const char *aptr, *ptr, *eptr;
4524 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
4525 *Ptr = StrBufNOTNULL;
4529 FlushStrBuf(LineBuf);
4531 ptr = aptr = Buf->buf;
4535 optr = LineBuf->buf;
4536 eptr = Buf->buf + Buf->BufUsed;
4537 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4539 while ((ptr <= eptr) &&
4546 LineBuf->BufUsed = optr - LineBuf->buf;
4547 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4548 optr = LineBuf->buf + LineBuf->BufUsed;
4549 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4553 if ((ptr >= eptr) && (optr > LineBuf->buf))
4555 LineBuf->BufUsed = optr - LineBuf->buf;
4557 if ((ptr <= eptr) && (*ptr == '\r'))
4559 if ((ptr <= eptr) && (*ptr == '\n'))
4566 *Ptr = StrBufNOTNULL;
4569 return Buf->BufUsed - (ptr - Buf->buf);
4574 * @ingroup StrBuf_IO
4575 * @brief removes double slashes from pathnames
4576 * @param Dir directory string to filter
4577 * @param RemoveTrailingSlash allows / disallows trailing slashes
4579 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4585 while (!IsEmptyStr(a)) {
4597 if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
4602 Dir->BufUsed = b - Dir->buf;