1 // Copyright (c) 1987-2022 by the citadel.org team
3 // This program is open source software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 3 of the License, or
6 // (at your option) any later version.
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 // GNU General Public License for more details.
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #include <sys/select.h>
27 #include <sys/types.h>
28 #define SHOW_ME_VAPPEND_PRINTF
31 #include "libcitadel.h"
47 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
48 const Bytef * source, uLong sourceLen, int level);
50 int BaseStrBufSize = 64;
52 int ZLibCompressionRatio = -1; /* defaults to 6 */
54 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
55 #define OS_CODE 0x03 /*< unix */
56 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
59 const char *StrBufNOTNULL = ((char*) NULL) - 1;
61 const char HexList[256][3] = {
62 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
63 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
64 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
65 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
66 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
67 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
68 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
69 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
70 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
71 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
72 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
73 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
74 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
75 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
76 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
77 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
81 * Private Structure for the Stringbuffer
84 char *buf; /**< the pointer to the dynamic buffer */
85 long BufSize; /**< how many spcae do we optain */
86 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
87 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
89 long nIncreases; /**< for profiling; cound how many times we needed more */
90 char bt [SIZ]; /**< Stacktrace of last increase */
91 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
96 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
97 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
100 #ifdef HAVE_BACKTRACE
101 static void StrBufBacktrace(StrBuf *Buf, int which) {
104 void *stack_frames[50];
109 pstart = pch = Buf->bt;
111 pstart = pch = Buf->bt_lastinc;
112 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
113 strings = backtrace_symbols(stack_frames, size);
114 for (i = 0; i < size; i++) {
116 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
118 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
128 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere) {
129 if (hFreeDbglog == -1) {
130 pid_t pid = getpid();
132 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
133 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
135 if ((*FreeMe)->nIncreases > 0) {
138 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
140 (*FreeMe)->nIncreases,
144 (*FreeMe)->bt_lastinc);
145 n = write(hFreeDbglog, buf, n);
150 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
154 n = write(hFreeDbglog, buf, n);
159 void dbg_IncreaseBuf(StrBuf *IncMe) {
161 #ifdef HAVE_BACKTRACE
162 StrBufBacktrace(Buf, 1);
167 void dbg_Init(StrBuf *Buf) {
170 Buf->bt_lastinc[0] = '\0';
171 #ifdef HAVE_BACKTRACE
172 StrBufBacktrace(Buf, 0);
178 #define dbg_FreeStrBuf(a, b)
179 #define dbg_IncreaseBuf(a)
185 * swaps the contents of two StrBufs
186 * this is to be used to have cheap switched between a work-buffer and a target buffer
190 static inline void iSwapBuffers(StrBuf *A, StrBuf *B) {
193 memcpy(&C, A, sizeof(*A));
194 memcpy(A, B, sizeof(*B));
195 memcpy(B, &C, sizeof(C));
200 void SwapBuffers(StrBuf *A, StrBuf *B) {
206 * Cast operator to Plain String
207 * @note if the buffer is altered by StrBuf operations, this pointer may become
208 * invalid. So don't lean on it after altering the buffer!
209 * Since this operation is considered cheap, rather call it often than risking
210 * your pointer to become invalid!
211 * Str the string we want to get the c-string representation for
212 * @returns the Pointer to the Content. Don't mess with it!
214 inline const char *ChrPtr(const StrBuf *Str) {
222 * since we know strlen()'s result, provide it here.
223 * Str the string to return the length to
224 * @returns contentlength of the buffer
226 inline int StrLength(const StrBuf *Str) {
227 return (Str != NULL) ? Str->BufUsed : 0;
231 // local utility function to resize the buffer
232 // Buf the buffer whichs storage we should increase
233 // KeepOriginal should we copy the original buffer or just start over with a new one
234 // DestSize what should fit in after?
235 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
237 size_t NewSize = Buf->BufSize * 2;
244 while ((NewSize <= DestSize) && (NewSize != 0)) {
253 NewBuf = (char*) malloc(NewSize);
254 if (NewBuf == NULL) {
258 if (KeepOriginal && (Buf->BufUsed > 0)) {
259 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
267 Buf->BufSize = NewSize;
269 dbg_IncreaseBuf(Buf);
275 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
276 // Buf Buffer to shrink (has to be empty)
277 // ThreshHold if the buffer is bigger then this, its readjusted
278 // NewSize if we Shrink it, how big are we going to be afterwards?
279 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
280 if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
282 Buf->buf = (char*) malloc(NewSize);
284 Buf->BufSize = NewSize;
290 * shrink long term buffers to their real size so they don't waste memory
291 * Buf buffer to shrink
292 * Force if not set, will just executed if the buffer is much to big; set for lifetime strings
293 * @returns physical size of the buffer
295 long StrBufShrinkToFit(StrBuf *Buf, int Force) {
298 if (Force || (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize)) {
301 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
305 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
306 Buf->BufSize = Buf->BufUsed + 1;
315 * Allocate a new buffer with default buffer size
316 * @returns the new stringbuffer
318 StrBuf *NewStrBuf(void) {
321 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
325 NewBuf->buf = (char*) malloc(BaseStrBufSize);
326 if (NewBuf->buf == NULL) {
330 NewBuf->buf[0] = '\0';
331 NewBuf->BufSize = BaseStrBufSize;
333 NewBuf->ConstBuf = 0;
341 * Copy Constructor; returns a duplicate of CopyMe
342 * CopyMe Buffer to faxmilate
343 * @returns the new stringbuffer
345 StrBuf *NewStrBufDup(const StrBuf *CopyMe) {
351 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
355 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
356 if (NewBuf->buf == NULL) {
361 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
362 NewBuf->BufUsed = CopyMe->BufUsed;
363 NewBuf->BufSize = CopyMe->BufSize;
364 NewBuf->ConstBuf = 0;
373 * Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
374 * NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
375 * CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
376 * CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
377 * KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
378 * @returns the new stringbuffer
380 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal) {
383 if (CreateRelpaceMe == NULL)
387 if (*CreateRelpaceMe != NULL)
388 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
390 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
394 if (CopyFlushMe == NULL) {
395 if (*CreateRelpaceMe != NULL)
396 FlushStrBuf(*CreateRelpaceMe);
398 *CreateRelpaceMe = NewStrBuf();
403 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
404 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
405 * be a big IO-Buffer...
407 if (KeepOriginal || (StrLength(CopyFlushMe) < 256)) {
408 if (*CreateRelpaceMe == NULL) {
409 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
413 NewBuf = *CreateRelpaceMe;
416 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
419 if (*CreateRelpaceMe == NULL) {
420 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
424 NewBuf = *CreateRelpaceMe;
426 iSwapBuffers (NewBuf, CopyFlushMe);
429 FlushStrBuf(CopyFlushMe);
436 * create a new Buffer using an existing c-string
437 * this function should also be used if you want to pre-suggest
438 * the buffer size to allocate in conjunction with ptr == NULL
439 * ptr the c-string to copy; may be NULL to create a blank instance
440 * nChars How many chars should we copy; -1 if we should measure the length ourselves
441 * @returns the new stringbuffer
443 StrBuf *NewStrBufPlain(const char* ptr, int nChars) {
445 size_t Siz = BaseStrBufSize;
448 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
453 CopySize = strlen((ptr != NULL)?ptr:"");
457 while ((Siz <= CopySize) && (Siz != 0))
465 NewBuf->buf = (char*) malloc(Siz);
466 if (NewBuf->buf == NULL) {
470 NewBuf->BufSize = Siz;
472 memcpy(NewBuf->buf, ptr, CopySize);
473 NewBuf->buf[CopySize] = '\0';
474 NewBuf->BufUsed = CopySize;
477 NewBuf->buf[0] = '\0';
480 NewBuf->ConstBuf = 0;
489 * Set an existing buffer from a c-string
491 * ptr c-string to put into
492 * nChars set to -1 if we should work 0-terminated
493 * @returns the new length of the string
495 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars) {
509 CopySize = strlen(ptr);
513 while ((Siz <= CopySize) && (Siz != 0))
521 if (Siz != Buf->BufSize)
522 IncreaseBuf(Buf, 0, Siz);
523 memcpy(Buf->buf, ptr, CopySize);
524 Buf->buf[CopySize] = '\0';
525 Buf->BufUsed = CopySize;
532 * use strbuf as wrapper for a string constant for easy handling
533 * StringConstant a string to wrap
534 * SizeOfStrConstant should be sizeof(StringConstant)-1
536 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
540 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
543 NewBuf->buf = (char*) StringConstant;
544 NewBuf->BufSize = SizeOfStrConstant;
545 NewBuf->BufUsed = SizeOfStrConstant;
546 NewBuf->ConstBuf = 1;
555 * flush the content of a Buf; keep its struct
556 * buf Buffer to flush
558 int FlushStrBuf(StrBuf *buf)
560 if ((buf == NULL) || (buf->buf == NULL))
570 * wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
573 int FLUSHStrBuf(StrBuf *buf)
579 if (buf->BufUsed > 0) {
580 memset(buf->buf, 0, buf->BufUsed);
587 int hFreeDbglog = -1;
591 * Its a double pointer, so it can NULL your pointer
592 * so fancy SIG11 appear instead of random results
593 * FreeMe Pointer Pointer to the buffer to free
595 void FreeStrBuf (StrBuf **FreeMe)
600 dbg_FreeStrBuf(FreeMe, 'F');
602 if (!(*FreeMe)->ConstBuf)
603 free((*FreeMe)->buf);
609 * flatten a Buffer to the Char * we return
610 * Its a double pointer, so it can NULL your pointer
611 * so fancy SIG11 appear instead of random results
612 * The Callee then owns the buffer and is responsible for freeing it.
613 * SmashMe Pointer Pointer to the buffer to release Buf from and free
614 * @returns the pointer of the buffer; Callee owns the memory thereafter.
616 char *SmashStrBuf (StrBuf **SmashMe) {
619 if ((SmashMe == NULL) || (*SmashMe == NULL))
622 dbg_FreeStrBuf(SmashMe, 'S');
624 Ret = (*SmashMe)->buf;
633 * If you want put your StrBuf into a Hash, use this as Destructor.
634 * VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
636 void HFreeStrBuf (void *VFreeMe) {
637 StrBuf *FreeMe = (StrBuf*)VFreeMe;
641 dbg_FreeStrBuf(SmashMe, 'H');
643 if (!FreeMe->ConstBuf)
649 /*******************************************************************************
650 * Simple string transformations *
651 *******************************************************************************/
654 * Wrapper around atol
656 long StrTol(const StrBuf *Buf)
661 return atol(Buf->buf);
667 * Wrapper around atoi
669 int StrToi(const StrBuf *Buf)
673 if (Buf->BufUsed > 0)
674 return atoi(Buf->buf);
680 * Checks to see if the string is a pure number
681 * Buf The buffer to inspect
682 * @returns 1 if its a pure number, 0, if not.
684 int StrBufIsNumber(const StrBuf *Buf) {
686 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
689 strtoll(Buf->buf, &pEnd, 10);
690 if (pEnd == Buf->buf)
692 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
694 if (Buf->buf == pEnd)
700 * modifies a Single char of the Buf
701 * You can point to it via char* or a zero-based integer
702 * Buf The buffer to manipulate
703 * ptr char* to zero; use NULL if unused
704 * nThChar zero based pointer into the string; use -1 if unused
705 * PeekValue The Character to place into the position
707 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
712 nThChar = ptr - Buf->buf;
713 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
715 Buf->buf[nThChar] = PeekValue;
720 * modifies a range of chars of the Buf
721 * You can point to it via char* or a zero-based integer
722 * Buf The buffer to manipulate
723 * ptr char* to zero; use NULL if unused
724 * nThChar zero based pointer into the string; use -1 if unused
725 * nChars how many chars are to be flushed?
726 * PookValue The Character to place into that area
728 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
733 nThChar = ptr - Buf->buf;
734 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
736 if (nThChar + nChars > Buf->BufUsed)
737 nChars = Buf->BufUsed - nThChar;
739 memset(Buf->buf + nThChar, PookValue, nChars);
740 /* just to be shure... */
741 Buf->buf[Buf->BufUsed] = 0;
746 * Append a StringBuffer to the buffer
747 * Buf Buffer to modify
748 * AppendBuf Buffer to copy at the end of our buffer
749 * Offset Should we start copying from an offset?
751 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
753 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
754 (Buf == NULL) || (Buf->buf == NULL))
757 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
758 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
760 memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
761 Buf->BufUsed += AppendBuf->BufUsed - Offset;
762 Buf->buf[Buf->BufUsed] = '\0';
766 // Append a C-String to the buffer
767 // Buf Buffer to modify
768 // AppendBuf Buffer to copy at the end of our buffer
769 // AppendSize number of bytes to copy; set to -1 if we should count it in advance
770 // Offset Should we start copying from an offset?
771 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
773 long BufSizeRequired;
775 if ((AppendBuf == NULL) || (Buf == NULL))
778 if (AppendSize < 0) {
779 aps = strlen(AppendBuf + Offset);
782 aps = AppendSize - Offset;
785 BufSizeRequired = Buf->BufUsed + aps + 1;
786 if (Buf->BufSize <= BufSizeRequired) {
787 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
790 memcpy(Buf->buf + Buf->BufUsed,
794 Buf->buf[Buf->BufUsed] = '\0';
798 * sprintf like function appending the formated string to the buffer
799 * vsnprintf version to wrap into own calls
800 * Buf Buffer to extend by format and Params
801 * format printf alike format to add
802 * ap va_list containing the items for format
804 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
812 if ((Buf == NULL) || (format == NULL))
815 BufSize = Buf->BufSize;
816 nWritten = Buf->BufSize + 1;
817 Offset = Buf->BufUsed;
818 newused = Offset + nWritten;
820 while (newused >= BufSize) {
822 nWritten = vsnprintf(Buf->buf + Offset,
823 Buf->BufSize - Offset,
826 newused = Offset + nWritten;
827 if (newused >= Buf->BufSize) {
828 if (IncreaseBuf(Buf, 1, newused) == -1)
829 return; /* TODO: error handling? */
830 newused = Buf->BufSize + 1;
833 Buf->BufUsed = Offset + nWritten;
834 BufSize = Buf->BufSize;
841 * sprintf like function appending the formated string to the buffer
842 * Buf Buffer to extend by format and Params
843 * format printf alike format to add
845 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
853 if ((Buf == NULL) || (format == NULL))
856 BufSize = Buf->BufSize;
857 nWritten = Buf->BufSize + 1;
858 Offset = Buf->BufUsed;
859 newused = Offset + nWritten;
861 while (newused >= BufSize) {
862 va_start(arg_ptr, format);
863 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
864 Buf->BufSize - Buf->BufUsed,
867 newused = Buf->BufUsed + nWritten;
868 if (newused >= Buf->BufSize) {
869 if (IncreaseBuf(Buf, 1, newused) == -1)
870 return; /* TODO: error handling? */
871 newused = Buf->BufSize + 1;
874 Buf->BufUsed += nWritten;
875 BufSize = Buf->BufSize;
882 * sprintf like function putting the formated string into the buffer
883 * Buf Buffer to extend by format and Parameters
884 * format printf alike format to add
886 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
891 if ((Buf == NULL) || (format == NULL))
894 nWritten = Buf->BufSize + 1;
895 while (nWritten >= Buf->BufSize) {
896 va_start(arg_ptr, format);
897 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
899 if (nWritten >= Buf->BufSize) {
900 if (IncreaseBuf(Buf, 0, 0) == -1)
901 return; /* TODO: error handling? */
902 nWritten = Buf->BufSize + 1;
905 Buf->BufUsed = nWritten ;
910 * Callback for cURL to append the webserver reply to a buffer
911 * ptr pre-defined by the cURL API; see man 3 curl for mre info
912 * size pre-defined by the cURL API; see man 3 curl for mre info
913 * nmemb pre-defined by the cURL API; see man 3 curl for mre info
914 * stream pre-defined by the cURL API; see man 3 curl for mre info
916 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
925 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
931 * extracts a substring from Source into dest
932 * dest buffer to place substring into
933 * Source string to copy substring from
934 * Offset chars to skip from start
935 * nChars number of chars to copy
936 * @returns the number of chars copied; may be different from nChars due to the size of Source
938 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
941 if (Offset > Source->BufUsed)
947 if (Offset + nChars < Source->BufUsed)
949 if ((nChars >= dest->BufSize) &&
950 (IncreaseBuf(dest, 0, nChars + 1) == -1))
952 memcpy(dest->buf, Source->buf + Offset, nChars);
953 dest->BufUsed = nChars;
954 dest->buf[dest->BufUsed] = '\0';
957 NCharsRemain = Source->BufUsed - Offset;
958 if ((NCharsRemain >= dest->BufSize) &&
959 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
961 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
962 dest->BufUsed = NCharsRemain;
963 dest->buf[dest->BufUsed] = '\0';
968 * Cut nChars from the start of the string
969 * Buf Buffer to modify
970 * nChars how many chars should be skipped?
972 void StrBufCutLeft(StrBuf *Buf, int nChars)
974 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
975 if (nChars >= Buf->BufUsed) {
979 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
980 Buf->BufUsed -= nChars;
981 Buf->buf[Buf->BufUsed] = '\0';
985 * Cut the trailing n Chars from the string
986 * Buf Buffer to modify
987 * nChars how many chars should be trunkated?
989 void StrBufCutRight(StrBuf *Buf, int nChars)
991 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
994 if (nChars >= Buf->BufUsed) {
998 Buf->BufUsed -= nChars;
999 Buf->buf[Buf->BufUsed] = '\0';
1003 * Cut the string after n Chars
1004 * Buf Buffer to modify
1005 * AfternChars after how many chars should we trunkate the string?
1006 * At if non-null and points inside of our string, cut it there.
1008 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1010 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1012 AfternChars = At - Buf->buf;
1015 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1017 Buf->BufUsed = AfternChars;
1018 Buf->buf[Buf->BufUsed] = '\0';
1023 * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1024 * Buf the string to modify
1026 void StrBufTrim(StrBuf *Buf)
1029 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1031 while ((Buf->BufUsed > 0) &&
1032 isspace(Buf->buf[Buf->BufUsed - 1]))
1036 Buf->buf[Buf->BufUsed] = '\0';
1038 if (Buf->BufUsed == 0) return;
1040 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1043 if (delta > 0) StrBufCutLeft(Buf, delta);
1046 * changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1047 * Buf the string to modify
1049 void StrBufSpaceToBlank(StrBuf *Buf)
1053 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1056 pche = pch + Buf->BufUsed;
1065 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1070 if ((Buf == NULL) || (Buf->buf == NULL)) {
1074 pRight = strchr(Buf->buf, rightboundary);
1075 if (pRight != NULL) {
1076 StrBufCutAt(Buf, 0, pRight);
1079 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1080 if (pLeft != NULL) {
1081 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1087 * uppercase the contents of a buffer
1088 * Buf the buffer to translate
1090 void StrBufUpCase(StrBuf *Buf)
1094 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1097 pche = pch + Buf->BufUsed;
1098 while (pch < pche) {
1099 *pch = toupper(*pch);
1106 * lowercase the contents of a buffer
1107 * Buf the buffer to translate
1109 void StrBufLowerCase(StrBuf *Buf)
1113 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1116 pche = pch + Buf->BufUsed;
1117 while (pch < pche) {
1118 *pch = tolower(*pch);
1124 /*******************************************************************************
1125 * a tokenizer that kills, maims, and destroys *
1126 *******************************************************************************/
1129 * Replace a token at a given place with a given length by another token with given length
1130 * Buf String where to work on
1131 * where where inside of the Buf is the search-token
1132 * HowLong How long is the token to be replaced
1133 * Repl Token to insert at 'where'
1134 * ReplLen Length of repl
1135 * @returns -1 if fail else length of resulting Buf
1137 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1138 const char *Repl, long ReplLen)
1141 if ((Buf == NULL) ||
1142 (where > Buf->BufUsed) ||
1143 (where + HowLong > Buf->BufUsed))
1146 if (where + ReplLen - HowLong > Buf->BufSize)
1147 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1150 memmove(Buf->buf + where + ReplLen,
1151 Buf->buf + where + HowLong,
1152 Buf->BufUsed - where - HowLong);
1154 memcpy(Buf->buf + where,
1157 Buf->BufUsed += ReplLen - HowLong;
1159 return Buf->BufUsed;
1163 * Counts the numbmer of tokens in a buffer
1164 * source String to count tokens in
1165 * tok Tokenizer char to count
1166 * @returns numbers of tokenizer chars found
1168 int StrBufNum_tokens(const StrBuf *source, char tok)
1172 if ((source == NULL) || (source->BufUsed == 0))
1174 if ((source->BufUsed == 1) && (*source->buf == tok))
1178 pche = pch + source->BufUsed;
1189 * a string tokenizer
1190 * Source StringBuffer to read into
1191 * parmnum n'th Parameter to remove
1192 * separator tokenizer character
1193 * @returns -1 if not found, else length of token.
1195 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1198 char *d, *s, *end; /* dest, source */
1201 /* Find desired eter */
1202 end = Source->buf + Source->BufUsed;
1204 while ((d <= end) &&
1207 /* End of string, bail! */
1212 if (*d == separator) {
1217 if ((d == NULL) || (d >= end))
1218 return 0; /* @Parameter not found */
1220 /* Find next eter */
1222 while ((s <= end) &&
1223 (*s && *s != separator))
1227 if (*s == separator)
1231 /* Hack and slash */
1236 memmove(d, s, Source->BufUsed - (s - Source->buf));
1237 Source->BufUsed += ReducedBy;
1238 Source->buf[Source->BufUsed] = '\0';
1240 else if (d == Source->buf) {
1242 Source->BufUsed = 0;
1246 Source->BufUsed += ReducedBy;
1257 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1259 const StrBuf Temp = {
1272 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1276 * a string tokenizer
1277 * dest Destination StringBuffer
1278 * Source StringBuffer to read into
1279 * parmnum n'th Parameter to extract
1280 * separator tokenizer character
1281 * @returns -1 if not found, else length of token.
1283 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1285 const char *s, *e; //* source * /
1286 int len = 0; //* running total length of extracted string * /
1287 int current_token = 0; //* token currently being processed * /
1290 dest->buf[0] = '\0';
1296 if ((Source == NULL) || (Source->BufUsed ==0)) {
1300 e = s + Source->BufUsed;
1303 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1305 while ((s < e) && !IsEmptyStr(s)) {
1306 if (*s == separator) {
1309 if (len >= dest->BufSize) {
1310 dest->BufUsed = len;
1311 if (IncreaseBuf(dest, 1, -1) < 0) {
1316 if ( (current_token == parmnum) &&
1317 (*s != separator)) {
1318 dest->buf[len] = *s;
1321 else if (current_token > parmnum) {
1327 dest->buf[len] = '\0';
1328 dest->BufUsed = len;
1330 if (current_token < parmnum) {
1331 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1334 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1343 * a string tokenizer to fetch an integer
1344 * Source String containing tokens
1345 * parmnum n'th Parameter to extract
1346 * separator tokenizer character
1347 * @returns 0 if not found, else integer representation of the token
1349 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1359 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1366 * a string tokenizer to fetch a long integer
1367 * Source String containing tokens
1368 * parmnum n'th Parameter to extract
1369 * separator tokenizer character
1370 * @returns 0 if not found, else long integer representation of the token
1372 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1382 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1390 * a string tokenizer to fetch an unsigned long
1391 * Source String containing tokens
1392 * parmnum n'th Parameter to extract
1393 * separator tokenizer character
1394 * @returns 0 if not found, else unsigned long representation of the token
1396 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1407 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1411 return (unsigned long) atol(pnum);
1420 * a string tokenizer; Bounds checker
1421 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1422 * Source our tokenbuffer
1423 * pStart the token iterator pointer to inspect
1424 * @returns whether the revolving pointer is inside of the search range
1426 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1428 if ((Source == NULL) ||
1429 (*pStart == StrBufNOTNULL) ||
1430 (Source->BufUsed == 0))
1434 if (*pStart == NULL)
1438 else if (*pStart > Source->buf + Source->BufUsed)
1442 else if (*pStart <= Source->buf)
1451 * a string tokenizer
1452 * dest Destination StringBuffer
1453 * Source StringBuffer to read into
1454 * pStart pointer to the end of the last token. Feed with NULL on start.
1455 * separator tokenizer
1456 * @returns -1 if not found, else length of token.
1458 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1460 const char *s; /* source */
1461 const char *EndBuffer; /* end stop of source buffer */
1462 int current_token = 0; /* token currently being processed */
1463 int len = 0; /* running total length of extracted string */
1465 if ((Source == NULL) ||
1466 (Source->BufUsed == 0) )
1468 *pStart = StrBufNOTNULL;
1474 EndBuffer = Source->buf + Source->BufUsed;
1478 dest->buf[0] = '\0';
1483 *pStart = EndBuffer + 1;
1487 if (*pStart == NULL)
1489 *pStart = Source->buf; /* we're starting to examine this buffer. */
1491 else if ((*pStart < Source->buf) ||
1492 (*pStart > EndBuffer ) )
1494 return -1; /* no more tokens to find. */
1498 /* start to find the next token */
1499 while ((s <= EndBuffer) &&
1500 (current_token == 0) )
1502 if (*s == separator)
1504 /* we found the next token */
1508 if (len >= dest->BufSize)
1510 /* our Dest-buffer isn't big enough, increase it. */
1511 dest->BufUsed = len;
1513 if (IncreaseBuf(dest, 1, -1) < 0) {
1514 /* WHUT? no more mem? bail out. */
1521 if ( (current_token == 0 ) && /* are we in our target token? */
1522 (!IsEmptyStr(s) ) &&
1523 (separator != *s) ) /* don't copy the token itself */
1525 dest->buf[len] = *s; /* Copy the payload */
1526 ++len; /* remember the bigger size. */
1532 /* did we reach the end? */
1533 if ((s > EndBuffer)) {
1534 EndBuffer = StrBufNOTNULL;
1535 *pStart = EndBuffer;
1538 *pStart = s; /* remember the position for the next run */
1541 /* sanitize our extracted token */
1542 dest->buf[len] = '\0';
1543 dest->BufUsed = len;
1550 * a string tokenizer
1551 * Source StringBuffer to read from
1552 * pStart pointer to the end of the last token. Feed with NULL.
1553 * separator tokenizer character
1554 * nTokens number of tokens to fastforward over
1555 * @returns -1 if not found, else length of token.
1557 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1559 const char *s, *EndBuffer; //* source * /
1560 int len = 0; //* running total length of extracted string * /
1561 int current_token = 0; //* token currently being processed * /
1563 if ((Source == NULL) ||
1564 (Source->BufUsed ==0)) {
1568 return Source->BufUsed;
1570 if (*pStart == NULL)
1571 *pStart = Source->buf;
1573 EndBuffer = Source->buf + Source->BufUsed;
1575 if ((*pStart < Source->buf) ||
1576 (*pStart > EndBuffer)) {
1584 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1586 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1587 if (*s == separator) {
1590 if (current_token >= nTokens) {
1602 * a string tokenizer to fetch an integer
1603 * Source StringBuffer to read from
1604 * pStart Cursor on the tokenstring
1605 * separator tokenizer character
1606 * @returns 0 if not found, else integer representation of the token
1608 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1618 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1625 * a string tokenizer to fetch a long integer
1626 * Source StringBuffer to read from
1627 * pStart Cursor on the tokenstring
1628 * separator tokenizer character
1629 * @returns 0 if not found, else long integer representation of the token
1631 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1641 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1649 * a string tokenizer to fetch an unsigned long
1650 * Source StringBuffer to read from
1651 * pStart Cursor on the tokenstring
1652 * separator tokenizer character
1653 * @returns 0 if not found, else unsigned long representation of the token
1655 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1666 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1670 return (unsigned long) atol(pnum);
1680 /*******************************************************************************
1681 * Escape Appending *
1682 *******************************************************************************/
1685 * Escape a string for feeding out as a URL while appending it to a Buffer
1686 * OutBuf the output buffer
1687 * In Buffer to encode
1688 * PlainIn way in from plain old c strings
1690 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1692 const char *pch, *pche;
1696 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1698 if (PlainIn != NULL) {
1699 len = strlen(PlainIn);
1705 pche = pch + In->BufUsed;
1712 pt = OutBuf->buf + OutBuf->BufUsed;
1713 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1715 while (pch < pche) {
1717 IncreaseBuf(OutBuf, 1, -1);
1718 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1719 pt = OutBuf->buf + OutBuf->BufUsed;
1722 if((*pch >= 'a' && *pch <= 'z') ||
1723 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1724 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1725 (*pch == '!') || (*pch == '_') ||
1726 (*pch == ',') || (*pch == '.'))
1733 *(pt + 1) = HexList[(unsigned char)*pch][0];
1734 *(pt + 2) = HexList[(unsigned char)*pch][1];
1736 OutBuf->BufUsed += 3;
1745 * Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1746 * OutBuf the output buffer
1747 * In Buffer to encode
1748 * PlainIn way in from plain old c strings
1750 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1751 const char *pch, *pche;
1755 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1757 if (PlainIn != NULL) {
1758 len = strlen(PlainIn);
1764 pche = pch + In->BufUsed;
1771 pt = OutBuf->buf + OutBuf->BufUsed;
1772 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1774 while (pch < pche) {
1776 IncreaseBuf(OutBuf, 1, -1);
1777 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1778 pt = OutBuf->buf + OutBuf->BufUsed;
1781 if((*pch >= 'a' && *pch <= 'z') ||
1782 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1783 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1784 (*pch == '!') || (*pch == '_') ||
1785 (*pch == ',') || (*pch == '.'))
1792 *(pt + 1) = HexList[(unsigned char)*pch][0];
1793 *(pt + 2) = HexList[(unsigned char)*pch][1];
1795 OutBuf->BufUsed += 3;
1803 * append a string with characters having a special meaning in xml encoded to the buffer
1804 * OutBuf the output buffer
1805 * In Buffer to encode
1806 * PlainIn way in from plain old c strings
1807 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1808 * OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1810 void StrBufXMLEscAppend(StrBuf *OutBuf,
1812 const char *PlainIn,
1814 int OverrideLowChars)
1816 const char *pch, *pche;
1821 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1823 if (PlainIn != NULL) {
1825 len = strlen((const char*)PlainIn);
1832 pch = (const char*)In->buf;
1833 pche = pch + In->BufUsed;
1840 pt = OutBuf->buf + OutBuf->BufUsed;
1841 /**< we max append 6 chars at once plus the \0 */
1842 pte = OutBuf->buf + OutBuf->BufSize - 6;
1844 while (pch < pche) {
1846 OutBuf->BufUsed = pt - OutBuf->buf;
1847 IncreaseBuf(OutBuf, 1, -1);
1848 pte = OutBuf->buf + OutBuf->BufSize - 6;
1849 /**< we max append 3 chars at once plus the \0 */
1851 pt = OutBuf->buf + OutBuf->BufUsed;
1855 memcpy(pt, HKEY("<"));
1859 else if (*pch == '>') {
1860 memcpy(pt, HKEY(">"));
1864 else if (*pch == '&') {
1865 memcpy(pt, HKEY("&"));
1869 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
1874 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
1877 while ((IsUtf8Sequence > 0) &&
1890 *pt = HexList[*(unsigned char*)pch][0];
1892 *pt = HexList[*(unsigned char*)pch][1];
1901 OutBuf->BufUsed = pt - OutBuf->buf;
1906 * append a string in hex encoding to the buffer
1907 * OutBuf the output buffer
1908 * In Buffer to encode
1909 * PlainIn way in from plain old c strings
1910 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1912 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1914 const unsigned char *pch, *pche;
1918 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1920 if (PlainIn != NULL) {
1922 len = strlen((const char*)PlainIn);
1929 pch = (const unsigned char*)In->buf;
1930 pche = pch + In->BufUsed;
1937 pt = OutBuf->buf + OutBuf->BufUsed;
1938 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1940 while (pch < pche) {
1942 IncreaseBuf(OutBuf, 1, -1);
1943 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1944 pt = OutBuf->buf + OutBuf->BufUsed;
1947 *pt = HexList[*pch][0];
1949 *pt = HexList[*pch][1];
1950 pt ++; pch ++; OutBuf->BufUsed += 2;
1956 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks) {
1962 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1964 if (PlainIn != NULL) {
1966 len = strlen(PlainIn);
1979 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
1981 if (ExpectLen > OutBuf->BufSize)
1982 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
1985 pt = OutBuf->buf + OutBuf->BufUsed;
1987 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
1990 OutBuf->BufUsed += len;
1995 // append a string in hex encoding to the buffer
1996 // OutBuf the output buffer
1997 // In Buffer to encode
1998 // PlainIn way in from plain old c strings
1999 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
2000 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2004 * Append a string, escaping characters which have meaning in HTML.
2006 * Target target buffer
2007 * Source source buffer; set to NULL if you just have a C-String
2008 * PlainIn Plain-C string to append; set to NULL if unused
2009 * nbsp If nonzero, spaces are converted to non-breaking spaces.
2010 * nolinebreaks if set to 1, linebreaks are removed from the string.
2011 * if set to 2, linebreaks are replaced by <br/>
2013 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2015 const char *aptr, *eiptr;
2019 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2022 if (PlainIn != NULL) {
2024 len = strlen(PlainIn);
2029 eiptr = aptr + Source->BufUsed;
2030 len = Source->BufUsed;
2036 bptr = Target->buf + Target->BufUsed;
2037 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2039 while (aptr < eiptr){
2041 IncreaseBuf(Target, 1, -1);
2042 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2043 bptr = Target->buf + Target->BufUsed;
2046 memcpy(bptr, "<", 4);
2048 Target->BufUsed += 4;
2050 else if (*aptr == '>') {
2051 memcpy(bptr, ">", 4);
2053 Target->BufUsed += 4;
2055 else if (*aptr == '&') {
2056 memcpy(bptr, "&", 5);
2058 Target->BufUsed += 5;
2060 else if (*aptr == '"') {
2061 memcpy(bptr, """, 6);
2063 Target->BufUsed += 6;
2065 else if (*aptr == '\'') {
2066 memcpy(bptr, "'", 5);
2068 Target->BufUsed += 5;
2070 else if (*aptr == LB) {
2075 else if (*aptr == RB) {
2080 else if (*aptr == QU) {
2085 else if ((*aptr == 32) && (nbsp == 1)) {
2086 memcpy(bptr, " ", 6);
2088 Target->BufUsed += 6;
2090 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2091 *bptr='\0'; /* nothing */
2093 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2094 memcpy(bptr, "<br/>", 11);
2096 Target->BufUsed += 11;
2100 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2101 *bptr='\0'; /* nothing */
2111 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2113 return Target->BufUsed;
2117 * Append a string, escaping characters which have meaning in HTML.
2118 * Converts linebreaks into blanks; escapes single quotes
2119 * Target target buffer
2120 * Source source buffer; set to NULL if you just have a C-String
2121 * PlainIn Plain-C string to append; set to NULL if unused
2123 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2125 const char *aptr, *eiptr;
2129 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2132 if (PlainIn != NULL) {
2134 len = strlen(PlainIn);
2139 eiptr = aptr + Source->BufUsed;
2140 len = Source->BufUsed;
2146 eptr = Target->buf + Target->BufSize - 8;
2147 tptr = Target->buf + Target->BufUsed;
2149 while (aptr < eiptr){
2151 IncreaseBuf(Target, 1, -1);
2152 eptr = Target->buf + Target->BufSize - 8;
2153 tptr = Target->buf + Target->BufUsed;
2156 if (*aptr == '\n') {
2160 else if (*aptr == '\r') {
2164 else if (*aptr == '\'') {
2170 Target->BufUsed += 5;
2183 * Append a string, escaping characters which have meaning in ICAL.
2185 * Target target buffer
2186 * Source source buffer; set to NULL if you just have a C-String
2187 * PlainIn Plain-C string to append; set to NULL if unused
2189 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2191 const char *aptr, *eiptr;
2195 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2198 if (PlainIn != NULL) {
2200 len = strlen(PlainIn);
2205 eiptr = aptr + Source->BufUsed;
2206 len = Source->BufUsed;
2212 eptr = Target->buf + Target->BufSize - 8;
2213 tptr = Target->buf + Target->BufUsed;
2215 while (aptr < eiptr){
2216 if(tptr + 3 >= eptr) {
2217 IncreaseBuf(Target, 1, -1);
2218 eptr = Target->buf + Target->BufSize - 8;
2219 tptr = Target->buf + Target->BufUsed;
2222 if (*aptr == '\n') {
2229 else if (*aptr == '\r') {
2236 else if (*aptr == ',') {
2252 * Append a string, escaping characters which have meaning in JavaScript strings .
2254 * Target target buffer
2255 * Source source buffer; set to NULL if you just have a C-String
2256 * PlainIn Plain-C string to append; set to NULL if unused
2257 * @returns size of result or -1
2259 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2261 const char *aptr, *eiptr;
2266 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2269 if (PlainIn != NULL) {
2271 len = strlen(PlainIn);
2276 eiptr = aptr + Source->BufUsed;
2277 len = Source->BufUsed;
2283 bptr = Target->buf + Target->BufUsed;
2284 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2286 while (aptr < eiptr){
2288 IncreaseBuf(Target, 1, -1);
2289 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2290 bptr = Target->buf + Target->BufUsed;
2294 memcpy(bptr, HKEY("\\n"));
2296 Target->BufUsed += 2;
2299 memcpy(bptr, HKEY("\\r"));
2301 Target->BufUsed += 2;
2308 Target->BufUsed += 2;
2311 if ((*(aptr + 1) == 'u') &&
2312 isxdigit(*(aptr + 2)) &&
2313 isxdigit(*(aptr + 3)) &&
2314 isxdigit(*(aptr + 4)) &&
2315 isxdigit(*(aptr + 5)))
2316 { /* oh, a unicode escaper. let it pass through. */
2317 memcpy(bptr, aptr, 6);
2320 Target->BufUsed += 6;
2328 Target->BufUsed += 2;
2336 Target->BufUsed += 2;
2343 Target->BufUsed += 2;
2350 Target->BufUsed += 2;
2353 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2354 while (IsUtf8Sequence > 0){
2357 if (--IsUtf8Sequence)
2365 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2367 return Target->BufUsed;
2371 * Append a string, escaping characters which have meaning in HTML + json.
2373 * Target target buffer
2374 * Source source buffer; set to NULL if you just have a C-String
2375 * PlainIn Plain-C string to append; set to NULL if unused
2376 * nbsp If nonzero, spaces are converted to non-breaking spaces.
2377 * nolinebreaks if set to 1, linebreaks are removed from the string.
2378 * if set to 2, linebreaks are replaced by <br/>
2380 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2382 const char *aptr, *eiptr;
2385 int IsUtf8Sequence = 0;
2387 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2390 if (PlainIn != NULL) {
2392 len = strlen(PlainIn);
2397 eiptr = aptr + Source->BufUsed;
2398 len = Source->BufUsed;
2404 bptr = Target->buf + Target->BufUsed;
2405 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2407 while (aptr < eiptr){
2409 IncreaseBuf(Target, 1, -1);
2410 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2411 bptr = Target->buf + Target->BufUsed;
2415 memcpy(bptr, HKEY("<"));
2417 Target->BufUsed += 4;
2420 memcpy(bptr, HKEY(">"));
2422 Target->BufUsed += 4;
2425 memcpy(bptr, HKEY("&"));
2427 Target->BufUsed += 5;
2440 switch (nolinebreaks) {
2442 *bptr='\0'; /* nothing */
2445 memcpy(bptr, HKEY("<br/>"));
2447 Target->BufUsed += 11;
2450 memcpy(bptr, HKEY("\\n"));
2452 Target->BufUsed += 2;
2456 switch (nolinebreaks) {
2459 *bptr='\0'; /* nothing */
2462 memcpy(bptr, HKEY("\\r"));
2464 Target->BufUsed += 2;
2474 Target->BufUsed += 2;
2477 if ((*(aptr + 1) == 'u') &&
2478 isxdigit(*(aptr + 2)) &&
2479 isxdigit(*(aptr + 3)) &&
2480 isxdigit(*(aptr + 4)) &&
2481 isxdigit(*(aptr + 5)))
2482 { /* oh, a unicode escaper. let it pass through. */
2483 memcpy(bptr, aptr, 6);
2486 Target->BufUsed += 6;
2494 Target->BufUsed += 2;
2502 Target->BufUsed += 2;
2509 Target->BufUsed += 2;
2516 Target->BufUsed += 2;
2520 memcpy(bptr, HKEY(" "));
2522 Target->BufUsed += 6;
2526 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2527 while (IsUtf8Sequence > 0){
2530 if (--IsUtf8Sequence)
2538 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2540 return Target->BufUsed;
2545 * replace all non-Ascii characters by another
2546 * Buf buffer to inspect
2547 * repl charater to stamp over non ascii chars
2549 void StrBufAsciify(StrBuf *Buf, const char repl) {
2552 for (offset = 0; offset < Buf->BufUsed; offset ++)
2553 if (!isascii(Buf->buf[offset]))
2554 Buf->buf[offset] = repl;
2559 * unhide special chars hidden to the HTML escaper
2560 * target buffer to put the unescaped string in
2561 * source buffer to unescape
2563 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) {
2567 if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2572 FlushStrBuf(target);
2574 len = source->BufUsed;
2575 for (a = 0; a < len; ++a) {
2576 if (target->BufUsed >= target->BufSize)
2577 IncreaseBuf(target, 1, -1);
2579 if (source->buf[a] == '=') {
2580 hex[0] = source->buf[a + 1];
2581 hex[1] = source->buf[a + 2];
2584 sscanf(hex, "%02x", &b);
2585 target->buf[target->BufUsed] = b;
2586 target->buf[++target->BufUsed] = 0;
2590 target->buf[target->BufUsed] = source->buf[a];
2591 target->buf[++target->BufUsed] = 0;
2598 * hide special chars from the HTML escapers and friends
2599 * target buffer to put the escaped string in
2600 * source buffer to escape
2602 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) {
2606 FlushStrBuf(target);
2608 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2613 len = source->BufUsed;
2614 for (i=0; i<len; ++i) {
2615 if (target->BufUsed + 4 >= target->BufSize)
2616 IncreaseBuf(target, 1, -1);
2617 if ( (isalnum(source->buf[i])) ||
2618 (source->buf[i]=='-') ||
2619 (source->buf[i]=='_') ) {
2620 target->buf[target->BufUsed++] = source->buf[i];
2623 sprintf(&target->buf[target->BufUsed],
2625 (0xFF &source->buf[i]));
2626 target->BufUsed += 3;
2629 target->buf[target->BufUsed + 1] = '\0';
2633 /*******************************************************************************
2634 * Quoted Printable de/encoding *
2635 *******************************************************************************/
2638 * decode a buffer from base 64 encoding; destroys original
2639 * Buf Buffor to transform
2641 int StrBufDecodeBase64(StrBuf *Buf) {
2649 xferbuf = (char*) malloc(Buf->BufSize);
2650 if (xferbuf == NULL)
2654 siz = CtdlDecodeBase64(xferbuf, Buf->buf, Buf->BufUsed);
2659 Buf->buf[Buf->BufUsed] = '\0';
2665 * decode a buffer from base 64 encoding; expects targetbuffer
2666 * BufIn Buffor to transform
2667 * BufOut Buffer to put result into
2669 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut) {
2670 if ((BufIn == NULL) || (BufOut == NULL))
2673 if (BufOut->BufSize < BufIn->BufUsed) {
2674 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2677 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf, BufIn->buf, BufIn->BufUsed);
2678 return BufOut->BufUsed;
2681 typedef struct __z_enc_stream {
2687 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err) {
2688 //base64_decodestate *state;;
2693 //case eBase64Decode:
2694 //case eBase64Encode:
2695 //state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2696 //base64_init_decodestate(state);
2697 //return (vStreamT*) state;
2702 z_enc_stream *stream;
2705 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2706 memset(stream, 0, sizeof(z_enc_stream));
2707 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2708 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2710 err = inflateInit(&stream->zstream);
2713 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2717 return (vStreamT*) stream;
2722 z_enc_stream *stream;
2725 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2726 memset(stream, 0, sizeof(z_enc_stream));
2727 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2728 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2729 /* write gzip header */
2730 stream->OutBuf.BufUsed = snprintf
2731 (stream->OutBuf.buf,
2732 stream->OutBuf.BufSize,
2733 "%c%c%c%c%c%c%c%c%c%c",
2734 gz_magic[0], gz_magic[1], Z_DEFLATED,
2735 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2738 err = deflateInit2(&stream->zstream,
2739 ZLibCompressionRatio,
2743 Z_DEFAULT_STRATEGY);
2745 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2749 return (vStreamT*) stream;
2760 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err) {
2765 if ((vStream == NULL) || (*vStream==NULL)) {
2766 *Err = strerror(EINVAL);
2771 //case eBase64Encode:
2772 //case eBase64Decode:
2778 z_enc_stream *stream = (z_enc_stream *)*vStream;
2779 (void)inflateEnd(&stream->zstream);
2780 free(stream->OutBuf.buf);
2785 z_enc_stream *stream = (z_enc_stream *)*vStream;
2786 err = deflateEnd(&stream->zstream);
2791 free(stream->OutBuf.buf);
2802 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err) {
2806 //case eBase64Encode:
2811 //case eBase64Decode:
2818 z_enc_stream *stream = (z_enc_stream *)vStream;
2819 int org_outbuf_len = stream->OutBuf.BufUsed;
2821 unsigned int chunkavail;
2823 if (In->ReadWritePointer != NULL)
2825 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2826 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
2827 (In->ReadWritePointer - In->Buf->buf);
2831 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2832 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2835 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2836 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2838 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
2840 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
2842 if (Target && (LastChunk || (stream->OutBuf.BufUsed != org_outbuf_len))) {
2843 iSwapBuffers(Target->Buf, &stream->OutBuf);
2846 if (stream->zstream.avail_in == 0) {
2847 FlushStrBuf(In->Buf);
2848 In->ReadWritePointer = NULL;
2851 if (stream->zstream.avail_in < 64) {
2852 memmove(In->Buf->buf,
2853 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2854 stream->zstream.avail_in);
2856 In->Buf->BufUsed = stream->zstream.avail_in;
2857 In->Buf->buf[In->Buf->BufUsed] = '\0';
2860 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2863 rc = (LastChunk && (err != Z_FINISH));
2864 if (!rc && (err != Z_OK)) {
2871 z_enc_stream *stream = (z_enc_stream *)vStream;
2872 int org_outbuf_len = stream->zstream.total_out;
2875 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
2876 if (In->ReadWritePointer != NULL) {
2877 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2878 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - (In->ReadWritePointer - In->Buf->buf);
2881 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2882 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2886 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2887 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2889 err = inflate(&stream->zstream, Z_NO_FLUSH);
2891 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
2894 err = Z_DATA_ERROR; /* and fall through */
2899 (void)inflateEnd(&stream->zstream);
2903 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
2905 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
2907 if (stream->zstream.avail_in == 0) {
2908 FlushStrBuf(In->Buf);
2909 In->ReadWritePointer = NULL;
2912 if (stream->zstream.avail_in < 64) {
2913 memmove(In->Buf->buf,
2914 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2915 stream->zstream.avail_in);
2917 In->Buf->BufUsed = stream->zstream.avail_in;
2918 In->Buf->buf[In->Buf->BufUsed] = '\0';
2922 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2936 * decode a buffer from base 64 encoding; destroys original
2937 * Buf Buffor to transform
2939 int StrBufDecodeHex(StrBuf *Buf) {
2941 char *pch, *pche, *pchi;
2943 if (Buf == NULL) return -1;
2945 pch = pchi = Buf->buf;
2946 pche = pch + Buf->BufUsed;
2948 while (pchi < pche){
2949 ch = decode_hex(pchi);
2956 Buf->BufUsed = pch - Buf->buf;
2957 return Buf->BufUsed;
2961 * replace all chars >0x20 && < 0x7F with Mute
2962 * Mute char to put over invalid chars
2963 * Buf Buffor to transform
2965 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2969 if (Buf == NULL) return -1;
2970 pch = (unsigned char *)Buf->buf;
2971 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2972 if ((*pch < 0x20) || (*pch > 0x7F))
2976 return Buf->BufUsed;
2981 * remove escaped strings from i.e. the url string (like %20 for blanks)
2982 * Buf Buffer to translate
2983 * StripBlanks Reduce several blanks to one?
2985 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2994 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2995 Buf->buf[Buf->BufUsed - 1] = '\0';
3000 while (a < Buf->BufUsed) {
3001 if (Buf->buf[a] == '+')
3003 else if (Buf->buf[a] == '%') {
3004 /* don't let % chars through, rather truncate the input. */
3005 if (a + 2 > Buf->BufUsed) {
3010 hex[0] = Buf->buf[a + 1];
3011 hex[1] = Buf->buf[a + 2];
3014 sscanf(hex, "%02x", &b);
3015 Buf->buf[a] = (char) b;
3016 len = Buf->BufUsed - a - 2;
3018 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3030 * RFC2047-encode a header field if necessary.
3031 * If no non-ASCII characters are found, the string
3032 * will be copied verbatim without encoding.
3034 * target Target buffer.
3035 * source Source string to be encoded.
3036 * @returns encoded length; -1 if non success.
3038 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3040 const char headerStr[] = "=?UTF-8?Q?";
3041 int need_to_encode = 0;
3045 if ((source == NULL) ||
3049 while ((i < source->BufUsed) &&
3050 (!IsEmptyStr (&source->buf[i])) &&
3051 (need_to_encode == 0)) {
3052 if (((unsigned char) source->buf[i] < 32) ||
3053 ((unsigned char) source->buf[i] > 126)) {
3059 if (!need_to_encode) {
3060 if (*target == NULL) {
3061 *target = NewStrBufPlain(source->buf, source->BufUsed);
3064 FlushStrBuf(*target);
3065 StrBufAppendBuf(*target, source, 0);
3068 return (*target)->BufUsed;
3072 if (*target == NULL)
3073 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3074 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3075 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3076 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3077 (*target)->BufUsed = sizeof(headerStr) - 1;
3078 for (i=0; (i < source->BufUsed); ++i) {
3079 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3080 IncreaseBuf(*target, 1, 0);
3081 ch = (unsigned char) source->buf[i];
3090 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3091 (*target)->BufUsed += 3;
3095 (*target)->buf[(*target)->BufUsed] = '_';
3097 (*target)->buf[(*target)->BufUsed] = ch;
3098 (*target)->BufUsed++;
3102 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3103 IncreaseBuf(*target, 1, 0);
3105 (*target)->buf[(*target)->BufUsed++] = '?';
3106 (*target)->buf[(*target)->BufUsed++] = '=';
3107 (*target)->buf[(*target)->BufUsed] = '\0';
3108 return (*target)->BufUsed;;
3112 * Quoted-Printable encode a message; make it < 80 columns width.
3113 * source Source string to be encoded.
3114 * @returns buffer with encoded message.
3116 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3120 const char *ptr, *eptr;
3124 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3126 OEptr = OutBuf->buf + OutBuf->BufSize;
3127 ptr = EncodeMe->buf;
3128 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3133 if (Optr + 4 >= OEptr)
3136 Offset = Optr - OutBuf->buf;
3137 OutBuf->BufUsed = Optr - OutBuf->buf;
3138 IncreaseBuf(OutBuf, 1, 0);
3139 Optr = OutBuf->buf + Offset;
3140 OEptr = OutBuf->buf + OutBuf->BufSize;
3144 /* ignore carriage returns */
3147 else if (*ptr == '\n') {
3148 /* hard line break */
3149 memcpy(Optr, HKEY("=0A"));
3154 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3155 ( (*ptr >= 62) && (*ptr <= 126) ))
3166 *Optr = HexList[ch][0];
3168 *Optr = HexList[ch][1];
3175 /* soft line break */
3176 if (isspace(*(Optr - 1))) {
3181 *Optr = HexList[ch][0];
3183 *Optr = HexList[ch][1];
3195 OutBuf->BufUsed = Optr - OutBuf->buf;
3201 static void AddRecipient(StrBuf *Target,
3203 StrBuf *EmailAddress,
3208 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3209 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3211 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3212 StrBufRFC2047encode(&EncBuf, UserName);
3213 StrBufAppendBuf(Target, EncBuf, 0);
3214 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3215 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3217 if (StrLength(EmailAddress) > 0){
3218 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3219 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3220 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3226 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3227 * \param Recp Source list of email recipients
3228 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3229 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3230 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3231 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3233 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3235 StrBuf *EmailAddress,
3239 const char *pch, *pche;
3240 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3242 if ((Recp == NULL) || (StrLength(Recp) == 0))
3246 pche = pch + StrLength(Recp);
3248 if (!CheckEncode(pch, -1, pche))
3249 return NewStrBufDup(Recp);
3251 Target = NewStrBufPlain(NULL, StrLength(Recp));
3253 while ((pch != NULL) && (pch < pche))
3255 while (isspace(*pch)) pch++;
3256 UserEnd = EmailStart = EmailEnd = NULL;
3258 if ((*pch == '"') || (*pch == '\'')) {
3259 UserStart = pch + 1;
3261 UserEnd = strchr(UserStart, *pch);
3262 if (UserEnd == NULL)
3263 break; ///TODO: Userfeedback??
3264 EmailStart = UserEnd + 1;
3265 while (isspace(*EmailStart))
3267 if (UserEnd == UserStart) {
3268 UserStart = UserEnd = NULL;
3271 if (*EmailStart == '<') {
3273 EmailEnd = strchr(EmailStart, '>');
3274 if (EmailEnd == NULL)
3275 EmailEnd = strchr(EmailStart, ',');
3279 EmailEnd = strchr(EmailStart, ',');
3281 if (EmailEnd == NULL)
3288 EmailEnd = strchr(UserStart, ',');
3289 if (EmailEnd == NULL) {
3290 EmailEnd = strchr(pch, '>');
3292 if (EmailEnd != NULL) {
3302 while ((EmailEnd > UserStart) && !gt &&
3303 ((*EmailEnd == ',') ||
3304 (*EmailEnd == '>') ||
3305 (isspace(*EmailEnd))))
3307 if (*EmailEnd == '>')
3312 if (EmailEnd == UserStart)
3316 EmailStart = strchr(UserStart, '<');
3317 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3319 UserEnd = EmailStart;
3321 while ((UserEnd > UserStart) &&
3322 isspace (*(UserEnd - 1)))
3325 if (UserStart >= UserEnd)
3326 UserStart = UserEnd = NULL;
3328 else { /* this is a local recipient... no domain, just a realname */
3329 EmailStart = UserStart;
3330 At = strchr(EmailStart, '@');
3336 EmailStart = UserStart;
3342 if ((UserStart != NULL) && (UserEnd != NULL))
3343 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3344 else if ((UserStart != NULL) && (UserEnd == NULL))
3345 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3347 FlushStrBuf(UserName);
3349 if ((EmailStart != NULL) && (EmailEnd != NULL))
3350 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3351 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3352 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3354 FlushStrBuf(EmailAddress);
3356 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3361 if ((pch != NULL) && (*pch == ','))
3363 if (pch != NULL) while (isspace(*pch))
3371 * replaces all occurances of 'search' by 'replace'
3372 * buf Buffer to modify
3373 * search character to search
3374 * replace character to replace search by
3376 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3381 for (i=0; i<buf->BufUsed; i++)
3382 if (buf->buf[i] == search)
3383 buf->buf[i] = replace;
3388 * removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3389 * buf Buffer to modify
3391 void StrBufToUnixLF(StrBuf *buf)
3393 char *pche, *pchS, *pchT;
3397 pche = buf->buf + buf->BufUsed;
3398 pchS = pchT = buf->buf;
3404 if (*pchS != '\n') {
3413 buf->BufUsed = pchT - buf->buf;
3417 /*******************************************************************************
3418 * Iconv Wrapper; RFC822 de/encoding *
3419 *******************************************************************************/
3422 * Wrapper around iconv_open()
3423 * Our version adds aliases for non-standard Microsoft charsets
3424 * such as 'MS950', aliasing them to names like 'CP950'
3426 * tocode Target encoding
3427 * fromcode Source encoding
3428 * pic anonimized pointer to iconv struct
3430 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3433 iconv_t ic = (iconv_t)(-1) ;
3434 ic = iconv_open(tocode, fromcode);
3435 if (ic == (iconv_t)(-1) ) {
3436 char alias_fromcode[64];
3437 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3438 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3439 alias_fromcode[0] = 'C';
3440 alias_fromcode[1] = 'P';
3441 ic = iconv_open(tocode, alias_fromcode);
3444 *(iconv_t *)pic = ic;
3450 * find one chunk of a RFC822 encoded string
3451 * Buffer where to search
3452 * bptr where to start searching
3453 * @returns found position, NULL if none.
3455 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3458 /* Find the next ?Q? */
3459 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3462 end = strchr(bptr + 2, '?');
3467 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3468 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3469 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3470 (*(end + 2) == '?')) {
3471 /* skip on to the end of the cluster, the next ?= */
3472 end = strstr(end + 3, "?=");
3475 /* sort of half valid encoding, try to find an end. */
3476 end = strstr(bptr, "?=");
3483 * convert one buffer according to the preselected iconv pointer PIC
3484 * ConvertBuf buffer we need to translate
3485 * TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3486 * pic Pointer to the iconv-session Object
3488 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3494 char *ibuf; /**< Buffer of characters to be converted */
3495 char *obuf; /**< Buffer for converted characters */
3496 size_t ibuflen; /**< Length of input buffer */
3497 size_t obuflen; /**< Length of output buffer */
3500 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3503 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3504 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3505 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3507 ic = *(iconv_t*)pic;
3508 ibuf = ConvertBuf->buf;
3509 ibuflen = ConvertBuf->BufUsed;
3511 obuflen = TmpBuf->BufSize;
3513 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3516 if (errno == E2BIG) {
3518 IncreaseBuf(TmpBuf, 0, 0);
3523 else if (errno == EILSEQ){
3524 /* hm, invalid utf8 sequence... what to do now? */
3525 /* An invalid multibyte sequence has been encountered in the input */
3527 else if (errno == EINVAL) {
3528 /* An incomplete multibyte sequence has been encountered in the input. */
3531 FlushStrBuf(TmpBuf);
3534 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3535 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3537 /* little card game: wheres the red lady? */
3538 iSwapBuffers(ConvertBuf, TmpBuf);
3539 FlushStrBuf(TmpBuf);
3546 * catches one RFC822 encoded segment, and decodes it.
3547 * Target buffer to fill with result
3548 * DecodeMe buffer with stuff to process
3549 * SegmentStart points to our current segment in DecodeMe
3550 * SegmentEnd Points to the end of our current segment in DecodeMe
3551 * ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3552 * ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3553 * FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3555 inline static void DecodeSegment(StrBuf *Target,
3556 const StrBuf *DecodeMe,
3557 const char *SegmentStart,
3558 const char *SegmentEnd,
3560 StrBuf *ConvertBuf2,
3561 StrBuf *FoundCharset)
3567 iconv_t ic = (iconv_t)(-1);
3571 /* Now we handle foreign character sets properly encoded
3572 * in RFC2047 format.
3574 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3575 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3576 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3577 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3578 if (FoundCharset != NULL) {
3579 FlushStrBuf(FoundCharset);
3580 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3582 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3583 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3585 *encoding = toupper(*encoding);
3586 if (*encoding == 'B') { /**< base64 */
3587 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3588 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3589 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3591 ConvertBuf->BufUsed);
3593 else if (*encoding == 'Q') { /**< quoted-printable */
3597 while (pos < ConvertBuf->BufUsed)
3599 if (ConvertBuf->buf[pos] == '_')
3600 ConvertBuf->buf[pos] = ' ';
3604 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3605 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3607 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3610 ConvertBuf->BufUsed);
3613 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3616 ctdl_iconv_open("UTF-8", charset, &ic);
3617 if (ic != (iconv_t)(-1) ) {
3619 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3620 StrBufAppendBuf(Target, ConvertBuf2, 0);
3625 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3631 * Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3632 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3633 * Target where to put the decoded string to
3634 * DecodeMe buffer with encoded string
3635 * DefaultCharset if we don't find one, which should we use?
3636 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3637 * put it here for later use where no string might be known.
3639 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3642 StrBuf *ConvertBuf2;
3643 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3644 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3646 StrBuf_RFC822_2_Utf8(Target,
3652 FreeStrBuf(&ConvertBuf);
3653 FreeStrBuf(&ConvertBuf2);
3657 * Handle subjects with RFC2047 encoding such as:
3658 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3659 * Target where to put the decoded string to
3660 * DecodeMe buffer with encoded string
3661 * DefaultCharset if we don't find one, which should we use?
3662 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3663 * put it here for later use where no string might be known.
3664 * ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3665 * ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3667 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3668 const StrBuf *DecodeMe,
3669 const StrBuf* DefaultCharset,
3670 StrBuf *FoundCharset,
3672 StrBuf *ConvertBuf2)
3674 StrBuf *DecodedInvalidBuf = NULL;
3675 const StrBuf *DecodeMee = DecodeMe;
3676 const char *start, *end, *next, *nextend, *ptr = NULL;
3678 iconv_t ic = (iconv_t)(-1) ;
3683 int illegal_non_rfc2047_encoding = 0;
3686 if (DecodeMe == NULL)
3688 /* Sometimes, badly formed messages contain strings which were simply
3689 * written out directly in some foreign character set instead of
3690 * using RFC2047 encoding. This is illegal but we will attempt to
3691 * handle it anyway by converting from a user-specified default
3692 * charset to UTF-8 if we see any nonprintable characters.
3695 for (i=0; i<DecodeMe->BufUsed; ++i) {
3696 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3697 illegal_non_rfc2047_encoding = 1;
3702 if ((illegal_non_rfc2047_encoding) &&
3703 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3704 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3707 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3708 if (ic != (iconv_t)(-1) ) {
3709 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3710 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3711 DecodeMee = DecodedInvalidBuf;
3717 /* pre evaluate the first pair */
3719 start = strstr(DecodeMee->buf, "=?");
3720 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3722 end = FindNextEnd (DecodeMee, start + 2);
3724 StrBufAppendBuf(Target, DecodeMee, 0);
3725 FreeStrBuf(&DecodedInvalidBuf);
3730 if (start != DecodeMee->buf) {
3733 nFront = start - DecodeMee->buf;
3734 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3737 * Since spammers will go to all sorts of absurd lengths to get their
3738 * messages through, there are LOTS of corrupt headers out there.
3739 * So, prevent a really badly formed RFC2047 header from throwing
3740 * this function into an infinite loop.
3742 while ((start != NULL) &&
3749 DecodeSegment(Target,
3757 next = strstr(end, "=?");
3759 if ((next != NULL) &&
3761 nextend = FindNextEnd(DecodeMee, next);
3762 if (nextend == NULL)
3765 /* did we find two partitions */
3766 if ((next != NULL) &&
3770 while ((ptr < next) &&
3777 * did we find a gab just filled with blanks?
3778 * if not, copy its stuff over.
3782 StrBufAppendBufPlain(Target,
3788 /* our next-pair is our new first pair now. */
3794 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3795 if ((end != NULL) && (end < nextend)) {
3797 while ( (ptr < nextend) &&
3804 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3806 FreeStrBuf(&DecodedInvalidBuf);
3809 /*******************************************************************************
3810 * Manipulating UTF-8 Strings *
3811 *******************************************************************************/
3814 * evaluate the length of an utf8 special character sequence
3815 * Char the character to examine
3816 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3818 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3821 unsigned char test = (1<<7);
3823 if ((*CharS & 0xC0) != 0xC0)
3827 ((test & ((unsigned char)*CharS)) != 0))
3832 if ((n > 6) || ((CharE - CharS) < n))
3838 * detect whether this char starts an utf-8 encoded char
3839 * Char character to inspect
3840 * @returns yes or no
3842 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3844 /** 11??.???? indicates an UTF8 Sequence. */
3845 return ((Char & 0xC0) == 0xC0);
3849 * measure the number of glyphs in an UTF8 string...
3850 * Buf string to measure
3851 * @returns the number of glyphs in Buf
3853 long StrBuf_Utf8StrLen(StrBuf *Buf)
3859 if ((Buf == NULL) || (Buf->BufUsed == 0))
3862 eptr = Buf->buf + Buf->BufUsed;
3863 while ((aptr < eptr) && (*aptr != '\0')) {
3864 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3865 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3866 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3878 * cuts a string after maxlen glyphs
3879 * Buf string to cut to maxlen glyphs
3880 * maxlen how long may the string become?
3881 * @returns current length of the string
3883 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3889 eptr = Buf->buf + Buf->BufUsed;
3890 while ((aptr < eptr) && (*aptr != '\0')) {
3891 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3892 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3893 while ((*aptr++ != '\0') && (m-- > 0));
3902 Buf->BufUsed = aptr - Buf->buf;
3903 return Buf->BufUsed;
3906 return Buf->BufUsed;
3914 /*******************************************************************************
3916 *******************************************************************************/
3919 * uses the same calling syntax as compress2(), but it
3920 * creates a stream compatible with HTTP "Content-encoding: gzip"
3921 * dest compressed buffer
3922 * destLen length of the compresed data
3923 * source source to encode
3924 * sourceLen length of source to encode
3925 * level compression level
3928 int ZEXPORT compress_gzip(Bytef * dest,
3930 const Bytef * source,
3934 /* write gzip header */
3935 snprintf((char *) dest, *destLen,
3936 "%c%c%c%c%c%c%c%c%c%c",
3937 gz_magic[0], gz_magic[1], Z_DEFLATED,
3938 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3941 /* normal deflate */
3944 stream.next_in = (Bytef *) source;
3945 stream.avail_in = (uInt) sourceLen;
3946 stream.next_out = dest + 10L; // after header
3947 stream.avail_out = (uInt) * destLen;
3948 if ((uLong) stream.avail_out != *destLen)
3951 stream.zalloc = (alloc_func) 0;
3952 stream.zfree = (free_func) 0;
3953 stream.opaque = (voidpf) 0;
3955 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3956 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3960 err = deflate(&stream, Z_FINISH);
3961 if (err != Z_STREAM_END) {
3962 deflateEnd(&stream);
3963 return err == Z_OK ? Z_BUF_ERROR : err;
3965 *destLen = stream.total_out + 10L;
3967 /* write CRC and Length */
3968 uLong crc = crc32(0L, source, sourceLen);
3970 for (n = 0; n < 4; ++n, ++*destLen) {
3971 dest[*destLen] = (int) (crc & 0xff);
3974 uLong len = stream.total_in;
3975 for (n = 0; n < 4; ++n, ++*destLen) {
3976 dest[*destLen] = (int) (len & 0xff);
3979 err = deflateEnd(&stream);
3986 * compress the buffer with gzip
3987 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3988 * Buf buffer whose content is to be gzipped
3990 int CompressBuffer(StrBuf *Buf)
3993 char *compressed_data = NULL;
3994 size_t compressed_len, bufsize;
3997 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3998 compressed_data = malloc(compressed_len);
4000 if (compressed_data == NULL)
4002 /* Flush some space after the used payload so valgrind shuts up... */
4003 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4004 Buf->buf[Buf->BufUsed + i++] = '\0';
4005 if (compress_gzip((Bytef *) compressed_data,
4008 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4011 Buf->buf = compressed_data;
4012 Buf->BufUsed = compressed_len;
4013 Buf->BufSize = bufsize;
4014 /* Flush some space after the used payload so valgrind shuts up... */
4016 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4017 Buf->buf[Buf->BufUsed + i++] = '\0';
4020 free(compressed_data);
4022 #endif /* HAVE_ZLIB */
4026 /*******************************************************************************
4027 * File I/O; Callbacks to libevent *
4028 *******************************************************************************/
4030 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4035 if ((FB == NULL) || (FB->Buf == NULL))
4039 * check whether the read pointer is somewhere in a range
4040 * where a cut left is inexpensive
4043 if (FB->ReadWritePointer != NULL)
4047 already_read = FB->ReadWritePointer - FB->Buf->buf;
4048 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4050 if (already_read != 0) {
4053 unread = FB->Buf->BufUsed - already_read;
4055 /* else nothing to compact... */
4057 FB->ReadWritePointer = FB->Buf->buf;
4058 bufremain = FB->Buf->BufSize;
4060 else if ((unread < 64) ||
4061 (bufremain < already_read))
4064 * if its just a tiny bit remaining, or we run out of space...
4067 FB->Buf->BufUsed = unread;
4068 if (unread < already_read)
4069 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4071 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4072 FB->ReadWritePointer = FB->Buf->buf;
4073 bufremain = FB->Buf->BufSize - unread - 1;
4075 else if (bufremain < (FB->Buf->BufSize / 10))
4077 /* get a bigger buffer */
4079 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4081 FB->ReadWritePointer = FB->Buf->buf + unread;
4083 bufremain = FB->Buf->BufSize - unread - 1;
4084 /*TODO: special increase function that won't copy the already read! */
4087 else if (bufremain < 10) {
4088 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4090 FB->ReadWritePointer = FB->Buf->buf;
4092 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4097 FB->ReadWritePointer = FB->Buf->buf;
4098 bufremain = FB->Buf->BufSize - 1;
4101 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4104 FB->Buf->BufUsed += n;
4105 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4110 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4115 if ((FB == NULL) || (FB->Buf == NULL))
4118 if (FB->ReadWritePointer != NULL)
4120 WriteRemain = FB->Buf->BufUsed -
4121 (FB->ReadWritePointer -
4125 FB->ReadWritePointer = FB->Buf->buf;
4126 WriteRemain = FB->Buf->BufUsed;
4129 n = write(fd, FB->ReadWritePointer, WriteRemain);
4131 FB->ReadWritePointer += n;
4133 if (FB->ReadWritePointer ==
4134 FB->Buf->buf + FB->Buf->BufUsed)
4136 FlushStrBuf(FB->Buf);
4137 FB->ReadWritePointer = NULL;
4140 // check whether we've got something to write
4141 // get the maximum chunk plus the pointer we can send
4142 // write whats there
4143 // if not all was sent, remember the send pointer for the next time
4144 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4150 * extract a "next line" from Buf; Ptr to persist across several iterations
4151 * LineBuf your line will be copied here.
4152 * FB BLOB with lines of text...
4153 * Ptr moved arround to keep the next-line across several iterations
4154 * has to be &NULL on start; will be &NotNULL on end of buffer
4155 * @returns size of copied buffer
4157 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4159 const char *aptr, *ptr, *eptr;
4162 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4166 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4167 FB->ReadWritePointer = StrBufNOTNULL;
4171 FlushStrBuf(LineBuf);
4172 if (FB->ReadWritePointer == NULL)
4173 ptr = aptr = FB->Buf->buf;
4175 ptr = aptr = FB->ReadWritePointer;
4177 optr = LineBuf->buf;
4178 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4179 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4181 while ((ptr <= eptr) &&
4188 LineBuf->BufUsed = optr - LineBuf->buf;
4189 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4190 optr = LineBuf->buf + LineBuf->BufUsed;
4191 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4196 if (optr > LineBuf->buf)
4198 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4199 LineBuf->BufUsed = optr - LineBuf->buf;
4201 if ((FB->ReadWritePointer != NULL) &&
4202 (FB->ReadWritePointer != FB->Buf->buf))
4204 /* Ok, the client application read all the data
4205 it was interested in so far. Since there is more to read,
4206 we now shrink the buffer, and move the rest over.
4208 StrBufCutLeft(FB->Buf,
4209 FB->ReadWritePointer - FB->Buf->buf);
4210 FB->ReadWritePointer = FB->Buf->buf;
4212 return eMustReadMore;
4215 LineBuf->BufUsed = optr - LineBuf->buf;
4217 if ((ptr <= eptr) && (*ptr == '\r'))
4219 if ((ptr <= eptr) && (*ptr == '\n'))
4223 FB->ReadWritePointer = ptr;
4226 FlushStrBuf(FB->Buf);
4227 FB->ReadWritePointer = NULL;
4230 return eReadSuccess;
4234 * check whether the chunk-buffer has more data waiting or not.
4235 * FB Chunk-Buffer to inspect
4237 eReadState StrBufCheckBuffer(IOBuffer *FB)
4241 if (FB->Buf->BufUsed == 0)
4242 return eReadSuccess;
4243 if (FB->ReadWritePointer == NULL)
4244 return eBufferNotEmpty;
4245 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4246 return eBufferNotEmpty;
4247 return eReadSuccess;
4250 long IOBufferStrLength(IOBuffer *FB)
4252 if ((FB == NULL) || (FB->Buf == NULL))
4254 if (FB->ReadWritePointer == NULL)
4255 return StrLength(FB->Buf);
4257 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4261 /*******************************************************************************
4262 * File I/O; Prefer buffered read since its faster! *
4263 *******************************************************************************/
4266 * Read a line from socket
4267 * flushes and closes the FD on error
4268 * buf the buffer to get the input to
4269 * fd pointer to the filedescriptor to read
4270 * append Append to an existing string or replace?
4271 * Error strerror() on error
4272 * @returns numbers of chars read
4274 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4276 int len, rlen, slen;
4278 if ((buf == NULL) || (buf->buf == NULL)) {
4279 *Error = strerror(EINVAL);
4286 slen = len = buf->BufUsed;
4288 rlen = read(*fd, &buf->buf[len], 1);
4290 *Error = strerror(errno);
4297 if (buf->buf[len] == '\n')
4299 if (buf->buf[len] != '\r')
4301 if (len + 2 >= buf->BufSize) {
4303 buf->buf[len+1] = '\0';
4304 IncreaseBuf(buf, 1, -1);
4308 buf->buf[len] = '\0';
4314 * Read a line from socket
4315 * flushes and closes the FD on error
4316 * Line the line to read from the fd / I/O Buffer
4317 * buf the buffer to get the input to
4318 * fd pointer to the filedescriptor to read
4319 * timeout number of successless selects until we bail out
4320 * selectresolution how long to wait on each select
4321 * Error strerror() on error
4322 * @returns numbers of chars read
4324 int StrBufTCP_read_buffered_line(StrBuf *Line,
4328 int selectresolution,
4332 int nSuccessLess = 0;
4339 if (buf->BufUsed > 0) {
4340 pch = strchr(buf->buf, '\n');
4343 len = pch - buf->buf;
4344 if (len > 0 && (*(pch - 1) == '\r') )
4346 StrBufSub(Line, buf, 0, len - rlen);
4347 StrBufCutLeft(buf, len + 1);
4352 if (buf->BufSize - buf->BufUsed < 10)
4353 IncreaseBuf(buf, 1, -1);
4355 fdflags = fcntl(*fd, F_GETFL);
4356 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4358 while ((nSuccessLess < timeout) && (pch == NULL)) {
4360 tv.tv_sec = selectresolution;
4365 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4366 *Error = strerror(errno);
4372 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4377 &buf->buf[buf->BufUsed],
4378 buf->BufSize - buf->BufUsed - 1);
4380 *Error = strerror(errno);
4385 else if (rlen > 0) {
4387 buf->BufUsed += rlen;
4388 buf->buf[buf->BufUsed] = '\0';
4389 pch = strchr(buf->buf, '\n');
4390 if ((pch == NULL) &&
4391 (buf->BufUsed + 10 > buf->BufSize) &&
4392 (IncreaseBuf(buf, 1, -1) == -1))
4400 len = pch - buf->buf;
4401 if (len > 0 && (*(pch - 1) == '\r') )
4403 StrBufSub(Line, buf, 0, len - rlen);
4404 StrBufCutLeft(buf, len + 1);
4411 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4412 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4413 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4415 * Read a line from socket
4416 * flushes and closes the FD on error
4417 * Line where to append our Line read from the fd / I/O Buffer;
4418 * IOBuf the buffer to get the input to; lifetime pair to FD
4419 * Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4420 * fd pointer to the filedescriptor to read
4421 * timeout number of successless selects until we bail out
4422 * selectresolution how long to wait on each select
4423 * Error strerror() on error
4424 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4426 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4431 int selectresolution,
4434 const char *pche = NULL;
4435 const char *pos = NULL;
4437 int len, rlen, retlen;
4438 int nSuccessLess = 0;
4440 const char *pch = NULL;
4446 if ((Line == NULL) ||
4453 *Error = ErrRBLF_PreConditionFailed;
4458 if ((IOBuf->BufUsed > 0) &&
4460 (pos < IOBuf->buf + IOBuf->BufUsed))
4464 pche = IOBuf->buf + IOBuf->BufUsed;
4468 while ((pch < pche) && (*pch != '\n'))
4470 if (Line->BufUsed + 10 > Line->BufSize)
4473 apos = pcht - Line->buf;
4475 IncreaseBuf(Line, 1, -1);
4476 pcht = Line->buf + apos;
4484 if (len > 0 && (*(pch - 1) == '\r') )
4493 if ((pch >= pche) || (*pch == '\0'))
4501 if ((pch != NULL) &&
4504 if (pch + 1 >= pche) {
4517 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4519 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4520 IncreaseBuf(IOBuf, 1, -1);
4522 fdflags = fcntl(*fd, F_GETFL);
4523 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4526 while ((nSuccessLess < timeout) &&
4536 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4537 *Error = strerror(errno);
4541 *Error = ErrRBLF_SelectFailed;
4544 if (! FD_ISSET(*fd, &rfds) != 0) {
4550 &IOBuf->buf[IOBuf->BufUsed],
4551 IOBuf->BufSize - IOBuf->BufUsed - 1);
4553 *Error = strerror(errno);
4558 else if (rlen > 0) {
4560 pLF = IOBuf->buf + IOBuf->BufUsed;
4561 IOBuf->BufUsed += rlen;
4562 IOBuf->buf[IOBuf->BufUsed] = '\0';
4564 pche = IOBuf->buf + IOBuf->BufUsed;
4566 while ((pLF < pche) && (*pLF != '\n'))
4568 if ((pLF >= pche) || (*pLF == '\0'))
4571 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4575 if (pLF != NULL) apos = pLF - IOBuf->buf;
4576 IncreaseBuf(IOBuf, 1, -1);
4577 if (pLF != NULL) pLF = IOBuf->buf + apos;
4591 if (len > 0 && (*(pLF - 1) == '\r') )
4593 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4594 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4600 return retlen + len;
4602 *Error = ErrRBLF_NotEnoughSentFromServer;
4607 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4609 * Input binary data from socket
4610 * flushes and closes the FD on error
4611 * Buf the buffer to get the input to
4612 * fd pointer to the filedescriptor to read
4613 * append Append to an existing string or replace?
4614 * nBytes the maximal number of bytes to read
4615 * Error strerror() on error
4616 * @returns numbers of chars read
4618 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4629 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4631 *Error = ErrRBLF_BLOBPreConditionFailed;
4636 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4637 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4639 ptr = Buf->buf + Buf->BufUsed;
4641 fdflags = fcntl(*fd, F_GETFL);
4642 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4644 while ((nRead < nBytes) &&
4654 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4655 *Error = strerror(errno);
4659 *Error = ErrRBLF_SelectFailed;
4662 if (! FD_ISSET(*fd, &rfds) != 0) {
4668 if ((rlen = read(*fd,
4670 nBytes - nRead)) == -1) {
4673 *Error = strerror(errno);
4678 Buf->BufUsed += rlen;
4680 Buf->buf[Buf->BufUsed] = '\0';
4684 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4685 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4687 * Input binary data from socket
4688 * flushes and closes the FD on error
4689 * Blob put binary thing here
4690 * IOBuf the buffer to get the input to
4691 * Pos offset inside of IOBuf
4692 * fd pointer to the filedescriptor to read
4693 * append Append to an existing string or replace?
4694 * nBytes the maximal number of bytes to read
4695 * check whether we should search for '000\n' terminators in case of timeouts
4696 * Error strerror() on error
4697 * @returns numbers of chars read
4699 int StrBufReadBLOBBuffered(StrBuf *Blob,
4712 int nAlreadyRead = 0;
4717 int nSuccessLess = 0;
4720 if ((Blob == NULL) ||
4727 *Error = ErrRBB_BLOBFPreConditionFailed;
4733 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4734 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4739 rlen = pos - IOBuf->buf;
4741 rlen = IOBuf->BufUsed - rlen;
4744 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed))
4746 if (rlen < nBytes) {
4747 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4748 Blob->BufUsed += rlen;
4749 Blob->buf[Blob->BufUsed] = '\0';
4750 nAlreadyRead = nRead = rlen;
4753 if (rlen >= nBytes) {
4754 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4755 Blob->BufUsed += nBytes;
4756 Blob->buf[Blob->BufUsed] = '\0';
4757 if (rlen == nBytes) {
4769 if (IOBuf->BufSize < nBytes - nRead) {
4770 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4774 fdflags = fcntl(*fd, F_GETFL);
4775 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4783 while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
4790 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4791 *Error = strerror(errno);
4794 if (*Error == NULL) {
4795 *Error = ErrRBLF_SelectFailed;
4799 if (! FD_ISSET(*fd, &rfds) != 0) {
4804 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
4805 // if (rlen == -1) { 2021feb27 ajc changed this, apparently we will always get at least 1 byte unless the connection is broken
4809 *Error = strerror(errno);
4812 else if (rlen == 0){
4813 if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
4814 StrBufPlain(Blob, HKEY("\n000\n"));
4815 StrBufCutRight(Blob, 5);
4816 return Blob->BufUsed;
4818 else if (!IsNonBlock)
4820 else if (nSuccessLess > MaxTries) {
4822 *Error = ErrRBB_too_many_selects;
4826 else if (rlen > 0) {
4830 IOBuf->BufUsed += rlen;
4833 if (nSuccessLess >= MaxTries) {
4835 *Error = ErrRBB_too_many_selects;
4839 if (nRead > nBytes) {
4840 *Pos = IOBuf->buf + nBytes;
4842 Blob->buf[Blob->BufUsed] = '\0';
4843 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4847 return nRead + nAlreadyRead;
4851 * extract a "next line" from Buf; Ptr to persist across several iterations
4852 * LineBuf your line will be copied here.
4853 * Buf BLOB with lines of text...
4854 * Ptr moved arround to keep the next-line across several iterations
4855 * has to be &NULL on start; will be &NotNULL on end of buffer
4856 * @returns size of remaining buffer
4858 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4860 const char *aptr, *ptr, *eptr;
4863 if ((Buf == NULL) ||
4864 (*Ptr == StrBufNOTNULL) ||
4866 (LineBuf->buf == NULL))
4868 *Ptr = StrBufNOTNULL;
4872 FlushStrBuf(LineBuf);
4874 ptr = aptr = Buf->buf;
4878 optr = LineBuf->buf;
4879 eptr = Buf->buf + Buf->BufUsed;
4880 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4882 while ((ptr <= eptr) &&
4889 LineBuf->BufUsed = optr - LineBuf->buf;
4890 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4891 optr = LineBuf->buf + LineBuf->BufUsed;
4892 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4896 if ((ptr >= eptr) && (optr > LineBuf->buf))
4898 LineBuf->BufUsed = optr - LineBuf->buf;
4900 if ((ptr <= eptr) && (*ptr == '\r'))
4902 if ((ptr <= eptr) && (*ptr == '\n'))
4909 *Ptr = StrBufNOTNULL;
4912 return Buf->BufUsed - (ptr - Buf->buf);
4917 * removes double slashes from pathnames
4918 * Dir directory string to filter
4919 * RemoveTrailingSlash allows / disallows trailing slashes
4921 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4927 while (!IsEmptyStr(a)) {
4939 if ((RemoveTrailingSlash) &&
4945 Dir->BufUsed = b - Dir->buf;
4950 * Decode a quoted-printable encoded StrBuf buffer "in place"
4951 * This is possible because the decoded will always be shorter than the encoded
4952 * so we don't have to worry about the buffer being to small.
4954 void StrBufDecodeQP(StrBuf *Buf)
4956 if (!Buf) { // sanity check #1
4960 int source_len = StrLength(Buf);
4961 if (source_len < 1) { // sanity check #2
4965 int spos = 0; // source position
4966 int tpos = 0; // target position
4968 while (spos < source_len) {
4969 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
4972 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
4975 else if (Buf->buf[spos] == '=') {
4978 sscanf(&Buf->buf[spos], "%02x", &ch);
4979 Buf->buf[tpos++] = ch;
4983 Buf->buf[tpos++] = Buf->buf[spos++];
4988 Buf->BufUsed = tpos;