1 // Copyright (c) 1987-2023 by the citadel.org team
3 // This program is open source software. Use, duplication, or disclosure
4 // is subject to the terms of the GNU General Public License, version 3.
14 #include <sys/select.h>
16 #include <sys/types.h>
17 #define SHOW_ME_VAPPEND_PRINTF
20 #include "libcitadel.h"
36 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen, const Bytef * source, uLong sourceLen, int level);
38 int BaseStrBufSize = 64;
40 int ZLibCompressionRatio = -1; /* defaults to 6 */
42 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
43 #define OS_CODE 0x03 /*< unix */
44 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
47 const char *StrBufNOTNULL = ((char*) NULL) - 1;
49 const char HexList[256][3] = {
50 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
51 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
52 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
53 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
54 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
55 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
56 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
57 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
58 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
59 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
60 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
61 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
62 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
63 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
64 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
65 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
69 * Private Structure for the Stringbuffer
72 char *buf; /**< the pointer to the dynamic buffer */
73 long BufSize; /**< how many spcae do we optain */
74 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
75 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
77 long nIncreases; /**< for profiling; cound how many times we needed more */
78 char bt [SIZ]; /**< Stacktrace of last increase */
79 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
84 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
85 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
89 static void StrBufBacktrace(StrBuf *Buf, int which) {
92 void *stack_frames[50];
97 pstart = pch = Buf->bt;
99 pstart = pch = Buf->bt_lastinc;
100 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
101 strings = backtrace_symbols(stack_frames, size);
102 for (i = 0; i < size; i++) {
104 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
106 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
116 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere) {
117 if (hFreeDbglog == -1) {
118 pid_t pid = getpid();
120 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
121 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
123 if ((*FreeMe)->nIncreases > 0) {
126 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
128 (*FreeMe)->nIncreases,
132 (*FreeMe)->bt_lastinc);
133 n = write(hFreeDbglog, buf, n);
138 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
142 n = write(hFreeDbglog, buf, n);
147 void dbg_IncreaseBuf(StrBuf *IncMe) {
149 #ifdef HAVE_BACKTRACE
150 StrBufBacktrace(Buf, 1);
155 void dbg_Init(StrBuf *Buf) {
158 Buf->bt_lastinc[0] = '\0';
159 #ifdef HAVE_BACKTRACE
160 StrBufBacktrace(Buf, 0);
166 #define dbg_FreeStrBuf(a, b)
167 #define dbg_IncreaseBuf(a)
173 * swaps the contents of two StrBufs
174 * this is to be used to have cheap switched between a work-buffer and a target buffer
178 static inline void iSwapBuffers(StrBuf *A, StrBuf *B) {
181 memcpy(&C, A, sizeof(*A));
182 memcpy(A, B, sizeof(*B));
183 memcpy(B, &C, sizeof(C));
188 void SwapBuffers(StrBuf *A, StrBuf *B) {
194 * Cast operator to Plain String
195 * @note if the buffer is altered by StrBuf operations, this pointer may become
196 * invalid. So don't lean on it after altering the buffer!
197 * Since this operation is considered cheap, rather call it often than risking
198 * your pointer to become invalid!
199 * Str the string we want to get the c-string representation for
200 * @returns the Pointer to the Content. Don't mess with it!
202 inline const char *ChrPtr(const StrBuf *Str) {
210 * since we know strlen()'s result, provide it here.
211 * Str the string to return the length to
212 * @returns contentlength of the buffer
214 inline int StrLength(const StrBuf *Str) {
215 return (Str != NULL) ? Str->BufUsed : 0;
219 // local utility function to resize the buffer
220 // Buf the buffer whichs storage we should increase
221 // KeepOriginal should we copy the original buffer or just start over with a new one
222 // DestSize what should fit in after?
223 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
225 size_t NewSize = Buf->BufSize * 2;
232 while ((NewSize <= DestSize) && (NewSize != 0)) {
241 NewBuf = (char*) malloc(NewSize);
242 if (NewBuf == NULL) {
246 if (KeepOriginal && (Buf->BufUsed > 0)) {
247 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
255 Buf->BufSize = NewSize;
257 dbg_IncreaseBuf(Buf);
263 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
264 // Buf Buffer to shrink (has to be empty)
265 // ThreshHold if the buffer is bigger then this, its readjusted
266 // NewSize if we Shrink it, how big are we going to be afterwards?
267 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
268 if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
270 Buf->buf = (char*) malloc(NewSize);
272 Buf->BufSize = NewSize;
278 * shrink long term buffers to their real size so they don't waste memory
279 * Buf buffer to shrink
280 * Force if not set, will just executed if the buffer is much to big; set for lifetime strings
281 * @returns physical size of the buffer
283 long StrBufShrinkToFit(StrBuf *Buf, int Force) {
286 if (Force || (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize)) {
289 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
293 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
294 Buf->BufSize = Buf->BufUsed + 1;
303 * Allocate a new buffer with default buffer size
304 * @returns the new stringbuffer
306 StrBuf *NewStrBuf(void) {
309 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
313 NewBuf->buf = (char*) malloc(BaseStrBufSize);
314 if (NewBuf->buf == NULL) {
318 NewBuf->buf[0] = '\0';
319 NewBuf->BufSize = BaseStrBufSize;
321 NewBuf->ConstBuf = 0;
329 * Copy Constructor; returns a duplicate of CopyMe
330 * CopyMe Buffer to faxmilate
331 * @returns the new stringbuffer
333 StrBuf *NewStrBufDup(const StrBuf *CopyMe) {
339 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
343 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
344 if (NewBuf->buf == NULL) {
349 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
350 NewBuf->BufUsed = CopyMe->BufUsed;
351 NewBuf->BufSize = CopyMe->BufSize;
352 NewBuf->ConstBuf = 0;
361 * Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
362 * NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
363 * CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
364 * CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
365 * KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
366 * @returns the new stringbuffer
368 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal) {
371 if (CreateRelpaceMe == NULL)
375 if (*CreateRelpaceMe != NULL)
376 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
378 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
382 if (CopyFlushMe == NULL) {
383 if (*CreateRelpaceMe != NULL)
384 FlushStrBuf(*CreateRelpaceMe);
386 *CreateRelpaceMe = NewStrBuf();
391 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
392 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
393 * be a big IO-Buffer...
395 if (KeepOriginal || (StrLength(CopyFlushMe) < 256)) {
396 if (*CreateRelpaceMe == NULL) {
397 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
401 NewBuf = *CreateRelpaceMe;
404 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
407 if (*CreateRelpaceMe == NULL) {
408 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
412 NewBuf = *CreateRelpaceMe;
414 iSwapBuffers (NewBuf, CopyFlushMe);
417 FlushStrBuf(CopyFlushMe);
424 * create a new Buffer using an existing c-string
425 * this function should also be used if you want to pre-suggest
426 * the buffer size to allocate in conjunction with ptr == NULL
427 * ptr the c-string to copy; may be NULL to create a blank instance
428 * nChars How many chars should we copy; -1 if we should measure the length ourselves
429 * @returns the new stringbuffer
431 StrBuf *NewStrBufPlain(const char* ptr, int nChars) {
433 size_t Siz = BaseStrBufSize;
436 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
441 CopySize = strlen((ptr != NULL)?ptr:"");
445 while ((Siz <= CopySize) && (Siz != 0))
453 NewBuf->buf = (char*) malloc(Siz);
454 if (NewBuf->buf == NULL) {
458 NewBuf->BufSize = Siz;
460 memcpy(NewBuf->buf, ptr, CopySize);
461 NewBuf->buf[CopySize] = '\0';
462 NewBuf->BufUsed = CopySize;
465 NewBuf->buf[0] = '\0';
468 NewBuf->ConstBuf = 0;
477 * Set an existing buffer from a c-string
479 * ptr c-string to put into
480 * nChars set to -1 if we should work 0-terminated
481 * @returns the new length of the string
483 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars) {
497 CopySize = strlen(ptr);
501 while ((Siz <= CopySize) && (Siz != 0))
509 if (Siz != Buf->BufSize)
510 IncreaseBuf(Buf, 0, Siz);
511 memcpy(Buf->buf, ptr, CopySize);
512 Buf->buf[CopySize] = '\0';
513 Buf->BufUsed = CopySize;
520 * use strbuf as wrapper for a string constant for easy handling
521 * StringConstant a string to wrap
522 * SizeOfStrConstant should be sizeof(StringConstant)-1
524 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
528 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
531 NewBuf->buf = (char*) StringConstant;
532 NewBuf->BufSize = SizeOfStrConstant;
533 NewBuf->BufUsed = SizeOfStrConstant;
534 NewBuf->ConstBuf = 1;
543 * flush the content of a Buf; keep its struct
544 * buf Buffer to flush
546 int FlushStrBuf(StrBuf *buf)
548 if ((buf == NULL) || (buf->buf == NULL))
558 * wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
561 int FLUSHStrBuf(StrBuf *buf)
567 if (buf->BufUsed > 0) {
568 memset(buf->buf, 0, buf->BufUsed);
575 int hFreeDbglog = -1;
579 * Its a double pointer, so it can NULL your pointer
580 * so fancy SIG11 appear instead of random results
581 * FreeMe Pointer Pointer to the buffer to free
583 void FreeStrBuf (StrBuf **FreeMe)
588 dbg_FreeStrBuf(FreeMe, 'F');
590 if (!(*FreeMe)->ConstBuf)
591 free((*FreeMe)->buf);
597 * flatten a Buffer to the Char * we return
598 * Its a double pointer, so it can NULL your pointer
599 * so fancy SIG11 appear instead of random results
600 * The Callee then owns the buffer and is responsible for freeing it.
601 * SmashMe Pointer Pointer to the buffer to release Buf from and free
602 * @returns the pointer of the buffer; Callee owns the memory thereafter.
604 char *SmashStrBuf (StrBuf **SmashMe) {
607 if ((SmashMe == NULL) || (*SmashMe == NULL))
610 dbg_FreeStrBuf(SmashMe, 'S');
612 Ret = (*SmashMe)->buf;
621 * If you want put your StrBuf into a Hash, use this as Destructor.
622 * VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
624 void HFreeStrBuf (void *VFreeMe) {
625 StrBuf *FreeMe = (StrBuf*)VFreeMe;
629 dbg_FreeStrBuf(SmashMe, 'H');
631 if (!FreeMe->ConstBuf)
637 /*******************************************************************************
638 * Simple string transformations *
639 *******************************************************************************/
642 * Wrapper around atol
644 long StrTol(const StrBuf *Buf)
649 return atol(Buf->buf);
655 * Wrapper around atoi
657 int StrToi(const StrBuf *Buf)
661 if (Buf->BufUsed > 0)
662 return atoi(Buf->buf);
668 * Checks to see if the string is a pure number
669 * Buf The buffer to inspect
670 * @returns 1 if its a pure number, 0, if not.
672 int StrBufIsNumber(const StrBuf *Buf) {
674 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
677 strtoll(Buf->buf, &pEnd, 10);
678 if (pEnd == Buf->buf)
680 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
682 if (Buf->buf == pEnd)
688 * modifies a Single char of the Buf
689 * You can point to it via char* or a zero-based integer
690 * Buf The buffer to manipulate
691 * ptr char* to zero; use NULL if unused
692 * nThChar zero based pointer into the string; use -1 if unused
693 * PeekValue The Character to place into the position
695 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
700 nThChar = ptr - Buf->buf;
701 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
703 Buf->buf[nThChar] = PeekValue;
708 * modifies a range of chars of the Buf
709 * You can point to it via char* or a zero-based integer
710 * Buf The buffer to manipulate
711 * ptr char* to zero; use NULL if unused
712 * nThChar zero based pointer into the string; use -1 if unused
713 * nChars how many chars are to be flushed?
714 * PookValue The Character to place into that area
716 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
721 nThChar = ptr - Buf->buf;
722 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
724 if (nThChar + nChars > Buf->BufUsed)
725 nChars = Buf->BufUsed - nThChar;
727 memset(Buf->buf + nThChar, PookValue, nChars);
728 /* just to be shure... */
729 Buf->buf[Buf->BufUsed] = 0;
734 * Append a StringBuffer to the buffer
735 * Buf Buffer to modify
736 * AppendBuf Buffer to copy at the end of our buffer
737 * Offset Should we start copying from an offset?
739 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
741 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
742 (Buf == NULL) || (Buf->buf == NULL))
745 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
746 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
748 memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
749 Buf->BufUsed += AppendBuf->BufUsed - Offset;
750 Buf->buf[Buf->BufUsed] = '\0';
754 // Append a C-String to the buffer
755 // Buf Buffer to modify
756 // AppendBuf Buffer to copy at the end of our buffer
757 // AppendSize number of bytes to copy; set to -1 if we should count it in advance
758 // Offset Should we start copying from an offset?
759 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
761 long BufSizeRequired;
763 if ((AppendBuf == NULL) || (Buf == NULL))
766 if (AppendSize < 0) {
767 aps = strlen(AppendBuf + Offset);
770 aps = AppendSize - Offset;
773 BufSizeRequired = Buf->BufUsed + aps + 1;
774 if (Buf->BufSize <= BufSizeRequired) {
775 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
778 memcpy(Buf->buf + Buf->BufUsed,
782 Buf->buf[Buf->BufUsed] = '\0';
786 * sprintf like function appending the formated string to the buffer
787 * vsnprintf version to wrap into own calls
788 * Buf Buffer to extend by format and Params
789 * format printf alike format to add
790 * ap va_list containing the items for format
792 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
800 if ((Buf == NULL) || (format == NULL))
803 BufSize = Buf->BufSize;
804 nWritten = Buf->BufSize + 1;
805 Offset = Buf->BufUsed;
806 newused = Offset + nWritten;
808 while (newused >= BufSize) {
810 nWritten = vsnprintf(Buf->buf + Offset,
811 Buf->BufSize - Offset,
814 newused = Offset + nWritten;
815 if (newused >= Buf->BufSize) {
816 if (IncreaseBuf(Buf, 1, newused) == -1)
817 return; /* TODO: error handling? */
818 newused = Buf->BufSize + 1;
821 Buf->BufUsed = Offset + nWritten;
822 BufSize = Buf->BufSize;
829 * sprintf like function appending the formated string to the buffer
830 * Buf Buffer to extend by format and Params
831 * format printf alike format to add
833 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
841 if ((Buf == NULL) || (format == NULL))
844 BufSize = Buf->BufSize;
845 nWritten = Buf->BufSize + 1;
846 Offset = Buf->BufUsed;
847 newused = Offset + nWritten;
849 while (newused >= BufSize) {
850 va_start(arg_ptr, format);
851 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
852 Buf->BufSize - Buf->BufUsed,
855 newused = Buf->BufUsed + nWritten;
856 if (newused >= Buf->BufSize) {
857 if (IncreaseBuf(Buf, 1, newused) == -1)
858 return; /* TODO: error handling? */
859 newused = Buf->BufSize + 1;
862 Buf->BufUsed += nWritten;
863 BufSize = Buf->BufSize;
870 * sprintf like function putting the formated string into the buffer
871 * Buf Buffer to extend by format and Parameters
872 * format printf alike format to add
874 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
879 if ((Buf == NULL) || (format == NULL))
882 nWritten = Buf->BufSize + 1;
883 while (nWritten >= Buf->BufSize) {
884 va_start(arg_ptr, format);
885 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
887 if (nWritten >= Buf->BufSize) {
888 if (IncreaseBuf(Buf, 0, 0) == -1)
889 return; /* TODO: error handling? */
890 nWritten = Buf->BufSize + 1;
893 Buf->BufUsed = nWritten ;
898 * Callback for cURL to append the webserver reply to a buffer
899 * ptr pre-defined by the cURL API; see man 3 curl for mre info
900 * size pre-defined by the cURL API; see man 3 curl for mre info
901 * nmemb pre-defined by the cURL API; see man 3 curl for mre info
902 * stream pre-defined by the cURL API; see man 3 curl for mre info
904 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
913 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
919 * extracts a substring from Source into dest
920 * dest buffer to place substring into
921 * Source string to copy substring from
922 * Offset chars to skip from start
923 * nChars number of chars to copy
924 * @returns the number of chars copied; may be different from nChars due to the size of Source
926 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
929 if (Offset > Source->BufUsed)
935 if (Offset + nChars < Source->BufUsed)
937 if ((nChars >= dest->BufSize) &&
938 (IncreaseBuf(dest, 0, nChars + 1) == -1))
940 memcpy(dest->buf, Source->buf + Offset, nChars);
941 dest->BufUsed = nChars;
942 dest->buf[dest->BufUsed] = '\0';
945 NCharsRemain = Source->BufUsed - Offset;
946 if ((NCharsRemain >= dest->BufSize) &&
947 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
949 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
950 dest->BufUsed = NCharsRemain;
951 dest->buf[dest->BufUsed] = '\0';
956 * Cut nChars from the start of the string
957 * Buf Buffer to modify
958 * nChars how many chars should be skipped?
960 void StrBufCutLeft(StrBuf *Buf, int nChars)
962 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
963 if (nChars >= Buf->BufUsed) {
967 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
968 Buf->BufUsed -= nChars;
969 Buf->buf[Buf->BufUsed] = '\0';
973 * Cut the trailing n Chars from the string
974 * Buf Buffer to modify
975 * nChars how many chars should be trunkated?
977 void StrBufCutRight(StrBuf *Buf, int nChars)
979 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
982 if (nChars >= Buf->BufUsed) {
986 Buf->BufUsed -= nChars;
987 Buf->buf[Buf->BufUsed] = '\0';
991 * Cut the string after n Chars
992 * Buf Buffer to modify
993 * AfternChars after how many chars should we trunkate the string?
994 * At if non-null and points inside of our string, cut it there.
996 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
998 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1000 AfternChars = At - Buf->buf;
1003 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1005 Buf->BufUsed = AfternChars;
1006 Buf->buf[Buf->BufUsed] = '\0';
1011 * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1012 * Buf the string to modify
1014 void StrBufTrim(StrBuf *Buf)
1017 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1019 while ((Buf->BufUsed > 0) &&
1020 isspace(Buf->buf[Buf->BufUsed - 1]))
1024 Buf->buf[Buf->BufUsed] = '\0';
1026 if (Buf->BufUsed == 0) return;
1028 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1031 if (delta > 0) StrBufCutLeft(Buf, delta);
1034 * changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1035 * Buf the string to modify
1037 void StrBufSpaceToBlank(StrBuf *Buf)
1041 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1044 pche = pch + Buf->BufUsed;
1053 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1058 if ((Buf == NULL) || (Buf->buf == NULL)) {
1062 pRight = strchr(Buf->buf, rightboundary);
1063 if (pRight != NULL) {
1064 StrBufCutAt(Buf, 0, pRight);
1067 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1068 if (pLeft != NULL) {
1069 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1075 * uppercase the contents of a buffer
1076 * Buf the buffer to translate
1078 void StrBufUpCase(StrBuf *Buf)
1082 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1085 pche = pch + Buf->BufUsed;
1086 while (pch < pche) {
1087 *pch = toupper(*pch);
1094 * lowercase the contents of a buffer
1095 * Buf the buffer to translate
1097 void StrBufLowerCase(StrBuf *Buf)
1101 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1104 pche = pch + Buf->BufUsed;
1105 while (pch < pche) {
1106 *pch = tolower(*pch);
1112 /*******************************************************************************
1113 * a tokenizer that kills, maims, and destroys *
1114 *******************************************************************************/
1117 * Replace a token at a given place with a given length by another token with given length
1118 * Buf String where to work on
1119 * where where inside of the Buf is the search-token
1120 * HowLong How long is the token to be replaced
1121 * Repl Token to insert at 'where'
1122 * ReplLen Length of repl
1123 * @returns -1 if fail else length of resulting Buf
1125 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1126 const char *Repl, long ReplLen)
1129 if ((Buf == NULL) ||
1130 (where > Buf->BufUsed) ||
1131 (where + HowLong > Buf->BufUsed))
1134 if (where + ReplLen - HowLong > Buf->BufSize)
1135 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1138 memmove(Buf->buf + where + ReplLen,
1139 Buf->buf + where + HowLong,
1140 Buf->BufUsed - where - HowLong);
1142 memcpy(Buf->buf + where,
1145 Buf->BufUsed += ReplLen - HowLong;
1147 return Buf->BufUsed;
1151 * Counts the numbmer of tokens in a buffer
1152 * source String to count tokens in
1153 * tok Tokenizer char to count
1154 * @returns numbers of tokenizer chars found
1156 int StrBufNum_tokens(const StrBuf *source, char tok)
1160 if ((source == NULL) || (source->BufUsed == 0))
1162 if ((source->BufUsed == 1) && (*source->buf == tok))
1166 pche = pch + source->BufUsed;
1177 * a string tokenizer
1178 * Source StringBuffer to read into
1179 * parmnum n'th Parameter to remove
1180 * separator tokenizer character
1181 * @returns -1 if not found, else length of token.
1183 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1186 char *d, *s, *end; /* dest, source */
1189 /* Find desired eter */
1190 end = Source->buf + Source->BufUsed;
1192 while ((d <= end) &&
1195 /* End of string, bail! */
1200 if (*d == separator) {
1205 if ((d == NULL) || (d >= end))
1206 return 0; /* @Parameter not found */
1208 /* Find next eter */
1210 while ((s <= end) &&
1211 (*s && *s != separator))
1215 if (*s == separator)
1219 /* Hack and slash */
1224 memmove(d, s, Source->BufUsed - (s - Source->buf));
1225 Source->BufUsed += ReducedBy;
1226 Source->buf[Source->BufUsed] = '\0';
1228 else if (d == Source->buf) {
1230 Source->BufUsed = 0;
1234 Source->BufUsed += ReducedBy;
1245 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1247 const StrBuf Temp = {
1260 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1264 * a string tokenizer
1265 * dest Destination StringBuffer
1266 * Source StringBuffer to read into
1267 * parmnum n'th Parameter to extract
1268 * separator tokenizer character
1269 * @returns -1 if not found, else length of token.
1271 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1273 const char *s, *e; //* source * /
1274 int len = 0; //* running total length of extracted string * /
1275 int current_token = 0; //* token currently being processed * /
1278 dest->buf[0] = '\0';
1284 if ((Source == NULL) || (Source->BufUsed ==0)) {
1288 e = s + Source->BufUsed;
1291 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1293 while ((s < e) && !IsEmptyStr(s)) {
1294 if (*s == separator) {
1297 if (len >= dest->BufSize) {
1298 dest->BufUsed = len;
1299 if (IncreaseBuf(dest, 1, -1) < 0) {
1304 if ( (current_token == parmnum) &&
1305 (*s != separator)) {
1306 dest->buf[len] = *s;
1309 else if (current_token > parmnum) {
1315 dest->buf[len] = '\0';
1316 dest->BufUsed = len;
1318 if (current_token < parmnum) {
1319 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1322 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1331 * a string tokenizer to fetch an integer
1332 * Source String containing tokens
1333 * parmnum n'th Parameter to extract
1334 * separator tokenizer character
1335 * @returns 0 if not found, else integer representation of the token
1337 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1347 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1354 * a string tokenizer to fetch a long integer
1355 * Source String containing tokens
1356 * parmnum n'th Parameter to extract
1357 * separator tokenizer character
1358 * @returns 0 if not found, else long integer representation of the token
1360 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1370 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1378 * a string tokenizer to fetch an unsigned long
1379 * Source String containing tokens
1380 * parmnum n'th Parameter to extract
1381 * separator tokenizer character
1382 * @returns 0 if not found, else unsigned long representation of the token
1384 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1395 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1399 return (unsigned long) atol(pnum);
1408 * a string tokenizer; Bounds checker
1409 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1410 * Source our tokenbuffer
1411 * pStart the token iterator pointer to inspect
1412 * @returns whether the revolving pointer is inside of the search range
1414 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1416 if ((Source == NULL) ||
1417 (*pStart == StrBufNOTNULL) ||
1418 (Source->BufUsed == 0))
1422 if (*pStart == NULL)
1426 else if (*pStart > Source->buf + Source->BufUsed)
1430 else if (*pStart <= Source->buf)
1439 * a string tokenizer
1440 * dest Destination StringBuffer
1441 * Source StringBuffer to read into
1442 * pStart pointer to the end of the last token. Feed with NULL on start.
1443 * separator tokenizer
1444 * @returns -1 if not found, else length of token.
1446 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1448 const char *s; /* source */
1449 const char *EndBuffer; /* end stop of source buffer */
1450 int current_token = 0; /* token currently being processed */
1451 int len = 0; /* running total length of extracted string */
1453 if ((Source == NULL) ||
1454 (Source->BufUsed == 0) )
1456 *pStart = StrBufNOTNULL;
1462 EndBuffer = Source->buf + Source->BufUsed;
1466 dest->buf[0] = '\0';
1471 *pStart = EndBuffer + 1;
1475 if (*pStart == NULL)
1477 *pStart = Source->buf; /* we're starting to examine this buffer. */
1479 else if ((*pStart < Source->buf) ||
1480 (*pStart > EndBuffer ) )
1482 return -1; /* no more tokens to find. */
1486 /* start to find the next token */
1487 while ((s <= EndBuffer) &&
1488 (current_token == 0) )
1490 if (*s == separator)
1492 /* we found the next token */
1496 if (len >= dest->BufSize)
1498 /* our Dest-buffer isn't big enough, increase it. */
1499 dest->BufUsed = len;
1501 if (IncreaseBuf(dest, 1, -1) < 0) {
1502 /* WHUT? no more mem? bail out. */
1509 if ( (current_token == 0 ) && /* are we in our target token? */
1510 (!IsEmptyStr(s) ) &&
1511 (separator != *s) ) /* don't copy the token itself */
1513 dest->buf[len] = *s; /* Copy the payload */
1514 ++len; /* remember the bigger size. */
1520 /* did we reach the end? */
1521 if ((s > EndBuffer)) {
1522 EndBuffer = StrBufNOTNULL;
1523 *pStart = EndBuffer;
1526 *pStart = s; /* remember the position for the next run */
1529 /* sanitize our extracted token */
1530 dest->buf[len] = '\0';
1531 dest->BufUsed = len;
1538 * a string tokenizer
1539 * Source StringBuffer to read from
1540 * pStart pointer to the end of the last token. Feed with NULL.
1541 * separator tokenizer character
1542 * nTokens number of tokens to fastforward over
1543 * @returns -1 if not found, else length of token.
1545 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1547 const char *s, *EndBuffer; //* source * /
1548 int len = 0; //* running total length of extracted string * /
1549 int current_token = 0; //* token currently being processed * /
1551 if ((Source == NULL) ||
1552 (Source->BufUsed ==0)) {
1556 return Source->BufUsed;
1558 if (*pStart == NULL)
1559 *pStart = Source->buf;
1561 EndBuffer = Source->buf + Source->BufUsed;
1563 if ((*pStart < Source->buf) ||
1564 (*pStart > EndBuffer)) {
1572 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1574 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1575 if (*s == separator) {
1578 if (current_token >= nTokens) {
1590 * a string tokenizer to fetch an integer
1591 * Source StringBuffer to read from
1592 * pStart Cursor on the tokenstring
1593 * separator tokenizer character
1594 * @returns 0 if not found, else integer representation of the token
1596 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1606 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1613 * a string tokenizer to fetch a long integer
1614 * Source StringBuffer to read from
1615 * pStart Cursor on the tokenstring
1616 * separator tokenizer character
1617 * @returns 0 if not found, else long integer representation of the token
1619 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1629 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1637 * a string tokenizer to fetch an unsigned long
1638 * Source StringBuffer to read from
1639 * pStart Cursor on the tokenstring
1640 * separator tokenizer character
1641 * @returns 0 if not found, else unsigned long representation of the token
1643 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1654 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1658 return (unsigned long) atol(pnum);
1668 /*******************************************************************************
1669 * Escape Appending *
1670 *******************************************************************************/
1673 * Escape a string for feeding out as a URL while appending it to a Buffer
1674 * OutBuf the output buffer
1675 * In Buffer to encode
1676 * PlainIn way in from plain old c strings
1678 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1680 const char *pch, *pche;
1684 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1686 if (PlainIn != NULL) {
1687 len = strlen(PlainIn);
1693 pche = pch + In->BufUsed;
1700 pt = OutBuf->buf + OutBuf->BufUsed;
1701 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1703 while (pch < pche) {
1705 IncreaseBuf(OutBuf, 1, -1);
1706 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1707 pt = OutBuf->buf + OutBuf->BufUsed;
1710 if((*pch >= 'a' && *pch <= 'z') ||
1711 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1712 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1713 (*pch == '!') || (*pch == '_') ||
1714 (*pch == ',') || (*pch == '.'))
1721 *(pt + 1) = HexList[(unsigned char)*pch][0];
1722 *(pt + 2) = HexList[(unsigned char)*pch][1];
1724 OutBuf->BufUsed += 3;
1733 * Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1734 * OutBuf the output buffer
1735 * In Buffer to encode
1736 * PlainIn way in from plain old c strings
1738 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1739 const char *pch, *pche;
1743 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1745 if (PlainIn != NULL) {
1746 len = strlen(PlainIn);
1752 pche = pch + In->BufUsed;
1759 pt = OutBuf->buf + OutBuf->BufUsed;
1760 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1762 while (pch < pche) {
1764 IncreaseBuf(OutBuf, 1, -1);
1765 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1766 pt = OutBuf->buf + OutBuf->BufUsed;
1769 if((*pch >= 'a' && *pch <= 'z') ||
1770 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1771 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1772 (*pch == '!') || (*pch == '_') ||
1773 (*pch == ',') || (*pch == '.'))
1780 *(pt + 1) = HexList[(unsigned char)*pch][0];
1781 *(pt + 2) = HexList[(unsigned char)*pch][1];
1783 OutBuf->BufUsed += 3;
1791 * append a string with characters having a special meaning in xml encoded to the buffer
1792 * OutBuf the output buffer
1793 * In Buffer to encode
1794 * PlainIn way in from plain old c strings
1795 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1796 * OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1798 void StrBufXMLEscAppend(StrBuf *OutBuf,
1800 const char *PlainIn,
1802 int OverrideLowChars)
1804 const char *pch, *pche;
1809 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1811 if (PlainIn != NULL) {
1813 len = strlen((const char*)PlainIn);
1820 pch = (const char*)In->buf;
1821 pche = pch + In->BufUsed;
1828 pt = OutBuf->buf + OutBuf->BufUsed;
1829 /**< we max append 6 chars at once plus the \0 */
1830 pte = OutBuf->buf + OutBuf->BufSize - 6;
1832 while (pch < pche) {
1834 OutBuf->BufUsed = pt - OutBuf->buf;
1835 IncreaseBuf(OutBuf, 1, -1);
1836 pte = OutBuf->buf + OutBuf->BufSize - 6;
1837 /**< we max append 3 chars at once plus the \0 */
1839 pt = OutBuf->buf + OutBuf->BufUsed;
1843 memcpy(pt, HKEY("<"));
1847 else if (*pch == '>') {
1848 memcpy(pt, HKEY(">"));
1852 else if (*pch == '&') {
1853 memcpy(pt, HKEY("&"));
1857 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
1862 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
1865 while ((IsUtf8Sequence > 0) &&
1878 *pt = HexList[*(unsigned char*)pch][0];
1880 *pt = HexList[*(unsigned char*)pch][1];
1889 OutBuf->BufUsed = pt - OutBuf->buf;
1894 * append a string in hex encoding to the buffer
1895 * OutBuf the output buffer
1896 * In Buffer to encode
1897 * PlainIn way in from plain old c strings
1898 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1900 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1902 const unsigned char *pch, *pche;
1906 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1908 if (PlainIn != NULL) {
1910 len = strlen((const char*)PlainIn);
1917 pch = (const unsigned char*)In->buf;
1918 pche = pch + In->BufUsed;
1925 pt = OutBuf->buf + OutBuf->BufUsed;
1926 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1928 while (pch < pche) {
1930 IncreaseBuf(OutBuf, 1, -1);
1931 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1932 pt = OutBuf->buf + OutBuf->BufUsed;
1935 *pt = HexList[*pch][0];
1937 *pt = HexList[*pch][1];
1938 pt ++; pch ++; OutBuf->BufUsed += 2;
1944 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks) {
1950 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1952 if (PlainIn != NULL) {
1954 len = strlen(PlainIn);
1967 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
1969 if (ExpectLen > OutBuf->BufSize)
1970 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
1973 pt = OutBuf->buf + OutBuf->BufUsed;
1975 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
1978 OutBuf->BufUsed += len;
1983 // append a string in hex encoding to the buffer
1984 // OutBuf the output buffer
1985 // In Buffer to encode
1986 // PlainIn way in from plain old c strings
1987 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1988 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1992 * Append a string, escaping characters which have meaning in HTML.
1994 * Target target buffer
1995 * Source source buffer; set to NULL if you just have a C-String
1996 * PlainIn Plain-C string to append; set to NULL if unused
1997 * nbsp If nonzero, spaces are converted to non-breaking spaces.
1998 * nolinebreaks if set to 1, linebreaks are removed from the string.
1999 * if set to 2, linebreaks are replaced by <br/>
2001 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2003 const char *aptr, *eiptr;
2007 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2010 if (PlainIn != NULL) {
2012 len = strlen(PlainIn);
2017 eiptr = aptr + Source->BufUsed;
2018 len = Source->BufUsed;
2024 bptr = Target->buf + Target->BufUsed;
2025 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2027 while (aptr < eiptr){
2029 IncreaseBuf(Target, 1, -1);
2030 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2031 bptr = Target->buf + Target->BufUsed;
2034 memcpy(bptr, "<", 4);
2036 Target->BufUsed += 4;
2038 else if (*aptr == '>') {
2039 memcpy(bptr, ">", 4);
2041 Target->BufUsed += 4;
2043 else if (*aptr == '&') {
2044 memcpy(bptr, "&", 5);
2046 Target->BufUsed += 5;
2048 else if (*aptr == '"') {
2049 memcpy(bptr, """, 6);
2051 Target->BufUsed += 6;
2053 else if (*aptr == '\'') {
2054 memcpy(bptr, "'", 5);
2056 Target->BufUsed += 5;
2058 else if (*aptr == LB) {
2063 else if (*aptr == RB) {
2068 else if (*aptr == QU) {
2073 else if ((*aptr == 32) && (nbsp == 1)) {
2074 memcpy(bptr, " ", 6);
2076 Target->BufUsed += 6;
2078 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2079 *bptr='\0'; /* nothing */
2081 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2082 memcpy(bptr, "<br/>", 11);
2084 Target->BufUsed += 11;
2088 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2089 *bptr='\0'; /* nothing */
2099 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2101 return Target->BufUsed;
2105 * Append a string, escaping characters which have meaning in HTML.
2106 * Converts linebreaks into blanks; escapes single quotes
2107 * Target target buffer
2108 * Source source buffer; set to NULL if you just have a C-String
2109 * PlainIn Plain-C string to append; set to NULL if unused
2111 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2113 const char *aptr, *eiptr;
2117 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2120 if (PlainIn != NULL) {
2122 len = strlen(PlainIn);
2127 eiptr = aptr + Source->BufUsed;
2128 len = Source->BufUsed;
2134 eptr = Target->buf + Target->BufSize - 8;
2135 tptr = Target->buf + Target->BufUsed;
2137 while (aptr < eiptr){
2139 IncreaseBuf(Target, 1, -1);
2140 eptr = Target->buf + Target->BufSize - 8;
2141 tptr = Target->buf + Target->BufUsed;
2144 if (*aptr == '\n') {
2148 else if (*aptr == '\r') {
2152 else if (*aptr == '\'') {
2158 Target->BufUsed += 5;
2171 * Append a string, escaping characters which have meaning in ICAL.
2173 * Target target buffer
2174 * Source source buffer; set to NULL if you just have a C-String
2175 * PlainIn Plain-C string to append; set to NULL if unused
2177 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2179 const char *aptr, *eiptr;
2183 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2186 if (PlainIn != NULL) {
2188 len = strlen(PlainIn);
2193 eiptr = aptr + Source->BufUsed;
2194 len = Source->BufUsed;
2200 eptr = Target->buf + Target->BufSize - 8;
2201 tptr = Target->buf + Target->BufUsed;
2203 while (aptr < eiptr){
2204 if(tptr + 3 >= eptr) {
2205 IncreaseBuf(Target, 1, -1);
2206 eptr = Target->buf + Target->BufSize - 8;
2207 tptr = Target->buf + Target->BufUsed;
2210 if (*aptr == '\n') {
2217 else if (*aptr == '\r') {
2224 else if (*aptr == ',') {
2240 * Append a string, escaping characters which have meaning in JavaScript strings .
2242 * Target target buffer
2243 * Source source buffer; set to NULL if you just have a C-String
2244 * PlainIn Plain-C string to append; set to NULL if unused
2245 * @returns size of result or -1
2247 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2249 const char *aptr, *eiptr;
2254 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2257 if (PlainIn != NULL) {
2259 len = strlen(PlainIn);
2264 eiptr = aptr + Source->BufUsed;
2265 len = Source->BufUsed;
2271 bptr = Target->buf + Target->BufUsed;
2272 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2274 while (aptr < eiptr){
2276 IncreaseBuf(Target, 1, -1);
2277 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2278 bptr = Target->buf + Target->BufUsed;
2282 memcpy(bptr, HKEY("\\n"));
2284 Target->BufUsed += 2;
2287 memcpy(bptr, HKEY("\\r"));
2289 Target->BufUsed += 2;
2296 Target->BufUsed += 2;
2299 if ((*(aptr + 1) == 'u') &&
2300 isxdigit(*(aptr + 2)) &&
2301 isxdigit(*(aptr + 3)) &&
2302 isxdigit(*(aptr + 4)) &&
2303 isxdigit(*(aptr + 5)))
2304 { /* oh, a unicode escaper. let it pass through. */
2305 memcpy(bptr, aptr, 6);
2308 Target->BufUsed += 6;
2316 Target->BufUsed += 2;
2324 Target->BufUsed += 2;
2331 Target->BufUsed += 2;
2338 Target->BufUsed += 2;
2341 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2342 while (IsUtf8Sequence > 0){
2345 if (--IsUtf8Sequence)
2353 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2355 return Target->BufUsed;
2359 * Append a string, escaping characters which have meaning in HTML + json.
2361 * Target target buffer
2362 * Source source buffer; set to NULL if you just have a C-String
2363 * PlainIn Plain-C string to append; set to NULL if unused
2364 * nbsp If nonzero, spaces are converted to non-breaking spaces.
2365 * nolinebreaks if set to 1, linebreaks are removed from the string.
2366 * if set to 2, linebreaks are replaced by <br/>
2368 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2370 const char *aptr, *eiptr;
2373 int IsUtf8Sequence = 0;
2375 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2378 if (PlainIn != NULL) {
2380 len = strlen(PlainIn);
2385 eiptr = aptr + Source->BufUsed;
2386 len = Source->BufUsed;
2392 bptr = Target->buf + Target->BufUsed;
2393 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2395 while (aptr < eiptr){
2397 IncreaseBuf(Target, 1, -1);
2398 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2399 bptr = Target->buf + Target->BufUsed;
2403 memcpy(bptr, HKEY("<"));
2405 Target->BufUsed += 4;
2408 memcpy(bptr, HKEY(">"));
2410 Target->BufUsed += 4;
2413 memcpy(bptr, HKEY("&"));
2415 Target->BufUsed += 5;
2428 switch (nolinebreaks) {
2430 *bptr='\0'; /* nothing */
2433 memcpy(bptr, HKEY("<br/>"));
2435 Target->BufUsed += 11;
2438 memcpy(bptr, HKEY("\\n"));
2440 Target->BufUsed += 2;
2444 switch (nolinebreaks) {
2447 *bptr='\0'; /* nothing */
2450 memcpy(bptr, HKEY("\\r"));
2452 Target->BufUsed += 2;
2462 Target->BufUsed += 2;
2465 if ((*(aptr + 1) == 'u') &&
2466 isxdigit(*(aptr + 2)) &&
2467 isxdigit(*(aptr + 3)) &&
2468 isxdigit(*(aptr + 4)) &&
2469 isxdigit(*(aptr + 5)))
2470 { /* oh, a unicode escaper. let it pass through. */
2471 memcpy(bptr, aptr, 6);
2474 Target->BufUsed += 6;
2482 Target->BufUsed += 2;
2490 Target->BufUsed += 2;
2497 Target->BufUsed += 2;
2504 Target->BufUsed += 2;
2508 memcpy(bptr, HKEY(" "));
2510 Target->BufUsed += 6;
2514 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2515 while (IsUtf8Sequence > 0){
2518 if (--IsUtf8Sequence)
2526 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2528 return Target->BufUsed;
2533 * replace all non-Ascii characters by another
2534 * Buf buffer to inspect
2535 * repl charater to stamp over non ascii chars
2537 void StrBufAsciify(StrBuf *Buf, const char repl) {
2540 for (offset = 0; offset < Buf->BufUsed; offset ++)
2541 if (!isascii(Buf->buf[offset]))
2542 Buf->buf[offset] = repl;
2547 * unhide special chars hidden to the HTML escaper
2548 * target buffer to put the unescaped string in
2549 * source buffer to unescape
2551 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) {
2555 if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2560 FlushStrBuf(target);
2562 len = source->BufUsed;
2563 for (a = 0; a < len; ++a) {
2564 if (target->BufUsed >= target->BufSize)
2565 IncreaseBuf(target, 1, -1);
2567 if (source->buf[a] == '=') {
2568 hex[0] = source->buf[a + 1];
2569 hex[1] = source->buf[a + 2];
2572 sscanf(hex, "%02x", &b);
2573 target->buf[target->BufUsed] = b;
2574 target->buf[++target->BufUsed] = 0;
2578 target->buf[target->BufUsed] = source->buf[a];
2579 target->buf[++target->BufUsed] = 0;
2586 * hide special chars from the HTML escapers and friends
2587 * target buffer to put the escaped string in
2588 * source buffer to escape
2590 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) {
2594 FlushStrBuf(target);
2596 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2601 len = source->BufUsed;
2602 for (i=0; i<len; ++i) {
2603 if (target->BufUsed + 4 >= target->BufSize)
2604 IncreaseBuf(target, 1, -1);
2605 if ( (isalnum(source->buf[i])) ||
2606 (source->buf[i]=='-') ||
2607 (source->buf[i]=='_') ) {
2608 target->buf[target->BufUsed++] = source->buf[i];
2611 sprintf(&target->buf[target->BufUsed],
2613 (0xFF &source->buf[i]));
2614 target->BufUsed += 3;
2617 target->buf[target->BufUsed + 1] = '\0';
2621 /*******************************************************************************
2622 * Quoted Printable de/encoding *
2623 *******************************************************************************/
2626 * decode a buffer from base 64 encoding; destroys original
2627 * Buf Buffor to transform
2629 int StrBufDecodeBase64(StrBuf *Buf) {
2637 xferbuf = (char*) malloc(Buf->BufSize);
2638 if (xferbuf == NULL)
2642 siz = CtdlDecodeBase64(xferbuf, Buf->buf, Buf->BufUsed);
2647 Buf->buf[Buf->BufUsed] = '\0';
2653 * decode a buffer from base 64 encoding; expects targetbuffer
2654 * BufIn Buffor to transform
2655 * BufOut Buffer to put result into
2657 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut) {
2658 if ((BufIn == NULL) || (BufOut == NULL))
2661 if (BufOut->BufSize < BufIn->BufUsed) {
2662 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2665 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf, BufIn->buf, BufIn->BufUsed);
2666 return BufOut->BufUsed;
2669 typedef struct __z_enc_stream {
2675 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err) {
2676 //base64_decodestate *state;;
2681 //case eBase64Decode:
2682 //case eBase64Encode:
2683 //state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2684 //base64_init_decodestate(state);
2685 //return (vStreamT*) state;
2690 z_enc_stream *stream;
2693 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2694 memset(stream, 0, sizeof(z_enc_stream));
2695 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2696 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2698 err = inflateInit(&stream->zstream);
2701 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2705 return (vStreamT*) stream;
2710 z_enc_stream *stream;
2713 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2714 memset(stream, 0, sizeof(z_enc_stream));
2715 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2716 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2717 /* write gzip header */
2718 stream->OutBuf.BufUsed = snprintf
2719 (stream->OutBuf.buf,
2720 stream->OutBuf.BufSize,
2721 "%c%c%c%c%c%c%c%c%c%c",
2722 gz_magic[0], gz_magic[1], Z_DEFLATED,
2723 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2726 err = deflateInit2(&stream->zstream,
2727 ZLibCompressionRatio,
2731 Z_DEFAULT_STRATEGY);
2733 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2737 return (vStreamT*) stream;
2748 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err) {
2753 if ((vStream == NULL) || (*vStream==NULL)) {
2754 *Err = strerror(EINVAL);
2759 //case eBase64Encode:
2760 //case eBase64Decode:
2766 z_enc_stream *stream = (z_enc_stream *)*vStream;
2767 (void)inflateEnd(&stream->zstream);
2768 free(stream->OutBuf.buf);
2773 z_enc_stream *stream = (z_enc_stream *)*vStream;
2774 err = deflateEnd(&stream->zstream);
2779 free(stream->OutBuf.buf);
2790 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err) {
2794 //case eBase64Encode:
2799 //case eBase64Decode:
2806 z_enc_stream *stream = (z_enc_stream *)vStream;
2807 int org_outbuf_len = stream->OutBuf.BufUsed;
2809 unsigned int chunkavail;
2811 if (In->ReadWritePointer != NULL)
2813 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2814 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
2815 (In->ReadWritePointer - In->Buf->buf);
2819 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2820 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2823 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2824 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2826 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
2828 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
2830 if (Target && (LastChunk || (stream->OutBuf.BufUsed != org_outbuf_len))) {
2831 iSwapBuffers(Target->Buf, &stream->OutBuf);
2834 if (stream->zstream.avail_in == 0) {
2835 FlushStrBuf(In->Buf);
2836 In->ReadWritePointer = NULL;
2839 if (stream->zstream.avail_in < 64) {
2840 memmove(In->Buf->buf,
2841 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2842 stream->zstream.avail_in);
2844 In->Buf->BufUsed = stream->zstream.avail_in;
2845 In->Buf->buf[In->Buf->BufUsed] = '\0';
2848 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2851 rc = (LastChunk && (err != Z_FINISH));
2852 if (!rc && (err != Z_OK)) {
2859 z_enc_stream *stream = (z_enc_stream *)vStream;
2860 int org_outbuf_len = stream->zstream.total_out;
2863 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
2864 if (In->ReadWritePointer != NULL) {
2865 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2866 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - (In->ReadWritePointer - In->Buf->buf);
2869 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2870 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2874 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2875 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2877 err = inflate(&stream->zstream, Z_NO_FLUSH);
2879 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
2882 err = Z_DATA_ERROR; /* and fall through */
2887 (void)inflateEnd(&stream->zstream);
2891 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
2893 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
2895 if (stream->zstream.avail_in == 0) {
2896 FlushStrBuf(In->Buf);
2897 In->ReadWritePointer = NULL;
2900 if (stream->zstream.avail_in < 64) {
2901 memmove(In->Buf->buf,
2902 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2903 stream->zstream.avail_in);
2905 In->Buf->BufUsed = stream->zstream.avail_in;
2906 In->Buf->buf[In->Buf->BufUsed] = '\0';
2910 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2924 * decode a buffer from base 64 encoding; destroys original
2925 * Buf Buffor to transform
2927 int StrBufDecodeHex(StrBuf *Buf) {
2929 char *pch, *pche, *pchi;
2931 if (Buf == NULL) return -1;
2933 pch = pchi = Buf->buf;
2934 pche = pch + Buf->BufUsed;
2936 while (pchi < pche){
2937 ch = decode_hex(pchi);
2944 Buf->BufUsed = pch - Buf->buf;
2945 return Buf->BufUsed;
2949 * replace all chars >0x20 && < 0x7F with Mute
2950 * Mute char to put over invalid chars
2951 * Buf Buffor to transform
2953 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2957 if (Buf == NULL) return -1;
2958 pch = (unsigned char *)Buf->buf;
2959 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2960 if ((*pch < 0x20) || (*pch > 0x7F))
2964 return Buf->BufUsed;
2969 * remove escaped strings from i.e. the url string (like %20 for blanks)
2970 * Buf Buffer to translate
2971 * StripBlanks Reduce several blanks to one?
2973 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2982 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2983 Buf->buf[Buf->BufUsed - 1] = '\0';
2988 while (a < Buf->BufUsed) {
2989 if (Buf->buf[a] == '+')
2991 else if (Buf->buf[a] == '%') {
2992 /* don't let % chars through, rather truncate the input. */
2993 if (a + 2 > Buf->BufUsed) {
2998 hex[0] = Buf->buf[a + 1];
2999 hex[1] = Buf->buf[a + 2];
3002 sscanf(hex, "%02x", &b);
3003 Buf->buf[a] = (char) b;
3004 len = Buf->BufUsed - a - 2;
3006 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3018 * RFC2047-encode a header field if necessary.
3019 * If no non-ASCII characters are found, the string
3020 * will be copied verbatim without encoding.
3022 * target Target buffer.
3023 * source Source string to be encoded.
3024 * @returns encoded length; -1 if non success.
3026 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3028 const char headerStr[] = "=?UTF-8?Q?";
3029 int need_to_encode = 0;
3033 if ((source == NULL) ||
3037 while ((i < source->BufUsed) &&
3038 (!IsEmptyStr (&source->buf[i])) &&
3039 (need_to_encode == 0)) {
3040 if (((unsigned char) source->buf[i] < 32) ||
3041 ((unsigned char) source->buf[i] > 126)) {
3047 if (!need_to_encode) {
3048 if (*target == NULL) {
3049 *target = NewStrBufPlain(source->buf, source->BufUsed);
3052 FlushStrBuf(*target);
3053 StrBufAppendBuf(*target, source, 0);
3056 return (*target)->BufUsed;
3060 if (*target == NULL)
3061 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3062 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3063 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3064 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3065 (*target)->BufUsed = sizeof(headerStr) - 1;
3066 for (i=0; (i < source->BufUsed); ++i) {
3067 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3068 IncreaseBuf(*target, 1, 0);
3069 ch = (unsigned char) source->buf[i];
3078 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3079 (*target)->BufUsed += 3;
3083 (*target)->buf[(*target)->BufUsed] = '_';
3085 (*target)->buf[(*target)->BufUsed] = ch;
3086 (*target)->BufUsed++;
3090 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3091 IncreaseBuf(*target, 1, 0);
3093 (*target)->buf[(*target)->BufUsed++] = '?';
3094 (*target)->buf[(*target)->BufUsed++] = '=';
3095 (*target)->buf[(*target)->BufUsed] = '\0';
3096 return (*target)->BufUsed;;
3099 // Quoted-Printable encode a message; make it < 80 columns width.
3100 // source Source string to be encoded.
3101 // returns buffer with encoded message.
3102 StrBuf *StrBufQuotedPrintableEncode(const StrBuf *EncodeMe) {
3105 const char *ptr, *eptr;
3109 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3111 OEptr = OutBuf->buf + OutBuf->BufSize;
3112 ptr = EncodeMe->buf;
3113 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3116 while (ptr < eptr) {
3117 if (Optr + 4 >= OEptr) {
3119 Offset = Optr - OutBuf->buf;
3120 OutBuf->BufUsed = Optr - OutBuf->buf;
3121 IncreaseBuf(OutBuf, 1, 0);
3122 Optr = OutBuf->buf + Offset;
3123 OEptr = OutBuf->buf + OutBuf->BufSize;
3125 if (*ptr == '\r') { // ignore carriage returns
3128 else if (*ptr == '\n') { // hard line break
3129 memcpy(Optr, HKEY("=0A"));
3134 else if (( (*ptr >= 32) && (*ptr <= 60) ) || ( (*ptr >= 62) && (*ptr <= 126) )) {
3144 *Optr = HexList[ch][0];
3146 *Optr = HexList[ch][1];
3152 if (LinePos > 72) { // soft line break
3153 if (isspace(*(Optr - 1))) {
3158 *Optr = HexList[ch][0];
3160 *Optr = HexList[ch][1];
3172 OutBuf->BufUsed = Optr - OutBuf->buf;
3178 static void AddRecipient(StrBuf *Target, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3181 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3182 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3184 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3185 StrBufRFC2047encode(&EncBuf, UserName);
3186 StrBufAppendBuf(Target, EncBuf, 0);
3187 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3188 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3190 if (StrLength(EmailAddress) > 0){
3191 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3192 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3193 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3199 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3200 * \param Recp Source list of email recipients
3201 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3202 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3203 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3204 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3206 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3208 const char *pch, *pche;
3209 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3211 if ((Recp == NULL) || (StrLength(Recp) == 0))
3215 pche = pch + StrLength(Recp);
3217 if (!CheckEncode(pch, -1, pche))
3218 return NewStrBufDup(Recp);
3220 Target = NewStrBufPlain(NULL, StrLength(Recp));
3222 while ((pch != NULL) && (pch < pche))
3224 while (isspace(*pch)) pch++;
3225 UserEnd = EmailStart = EmailEnd = NULL;
3227 if ((*pch == '"') || (*pch == '\'')) {
3228 UserStart = pch + 1;
3230 UserEnd = strchr(UserStart, *pch);
3231 if (UserEnd == NULL)
3232 break; ///TODO: Userfeedback??
3233 EmailStart = UserEnd + 1;
3234 while (isspace(*EmailStart))
3236 if (UserEnd == UserStart) {
3237 UserStart = UserEnd = NULL;
3240 if (*EmailStart == '<') {
3242 EmailEnd = strchr(EmailStart, '>');
3243 if (EmailEnd == NULL)
3244 EmailEnd = strchr(EmailStart, ',');
3248 EmailEnd = strchr(EmailStart, ',');
3250 if (EmailEnd == NULL)
3257 EmailEnd = strchr(UserStart, ',');
3258 if (EmailEnd == NULL) {
3259 EmailEnd = strchr(pch, '>');
3261 if (EmailEnd != NULL) {
3271 while ((EmailEnd > UserStart) && !gt &&
3272 ((*EmailEnd == ',') ||
3273 (*EmailEnd == '>') ||
3274 (isspace(*EmailEnd))))
3276 if (*EmailEnd == '>')
3281 if (EmailEnd == UserStart)
3285 EmailStart = strchr(UserStart, '<');
3286 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3288 UserEnd = EmailStart;
3290 while ((UserEnd > UserStart) &&
3291 isspace (*(UserEnd - 1)))
3294 if (UserStart >= UserEnd)
3295 UserStart = UserEnd = NULL;
3297 else { /* this is a local recipient... no domain, just a realname */
3298 EmailStart = UserStart;
3299 At = strchr(EmailStart, '@');
3305 EmailStart = UserStart;
3311 if ((UserStart != NULL) && (UserEnd != NULL))
3312 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3313 else if ((UserStart != NULL) && (UserEnd == NULL))
3314 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3316 FlushStrBuf(UserName);
3318 if ((EmailStart != NULL) && (EmailEnd != NULL))
3319 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3320 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3321 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3323 FlushStrBuf(EmailAddress);
3325 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3330 if ((pch != NULL) && (*pch == ','))
3332 if (pch != NULL) while (isspace(*pch))
3340 * replaces all occurances of 'search' by 'replace'
3341 * buf Buffer to modify
3342 * search character to search
3343 * replace character to replace search by
3345 void StrBufReplaceChars(StrBuf *buf, char search, char replace) {
3349 for (i=0; i<buf->BufUsed; i++)
3350 if (buf->buf[i] == search)
3351 buf->buf[i] = replace;
3356 * removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3357 * buf Buffer to modify
3359 void StrBufToUnixLF(StrBuf *buf)
3361 char *pche, *pchS, *pchT;
3365 pche = buf->buf + buf->BufUsed;
3366 pchS = pchT = buf->buf;
3372 if (*pchS != '\n') {
3381 buf->BufUsed = pchT - buf->buf;
3385 /*******************************************************************************
3386 * Iconv Wrapper; RFC822 de/encoding *
3387 *******************************************************************************/
3390 * Wrapper around iconv_open()
3391 * Our version adds aliases for non-standard Microsoft charsets
3392 * such as 'MS950', aliasing them to names like 'CP950'
3394 * tocode Target encoding
3395 * fromcode Source encoding
3396 * pic anonimized pointer to iconv struct
3398 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3401 iconv_t ic = (iconv_t)(-1) ;
3402 ic = iconv_open(tocode, fromcode);
3403 if (ic == (iconv_t)(-1) ) {
3404 char alias_fromcode[64];
3405 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3406 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3407 alias_fromcode[0] = 'C';
3408 alias_fromcode[1] = 'P';
3409 ic = iconv_open(tocode, alias_fromcode);
3412 *(iconv_t *)pic = ic;
3418 * find one chunk of a RFC822 encoded string
3419 * Buffer where to search
3420 * bptr where to start searching
3421 * @returns found position, NULL if none.
3423 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3426 /* Find the next ?Q? */
3427 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3430 end = strchr(bptr + 2, '?');
3435 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3436 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3437 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3438 (*(end + 2) == '?')) {
3439 /* skip on to the end of the cluster, the next ?= */
3440 end = strstr(end + 3, "?=");
3443 /* sort of half valid encoding, try to find an end. */
3444 end = strstr(bptr, "?=");
3451 * convert one buffer according to the preselected iconv pointer PIC
3452 * ConvertBuf buffer we need to translate
3453 * TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3454 * pic Pointer to the iconv-session Object
3456 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3462 char *ibuf; /**< Buffer of characters to be converted */
3463 char *obuf; /**< Buffer for converted characters */
3464 size_t ibuflen; /**< Length of input buffer */
3465 size_t obuflen; /**< Length of output buffer */
3468 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3471 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3472 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3473 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3475 ic = *(iconv_t*)pic;
3476 ibuf = ConvertBuf->buf;
3477 ibuflen = ConvertBuf->BufUsed;
3479 obuflen = TmpBuf->BufSize;
3481 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3484 if (errno == E2BIG) {
3486 IncreaseBuf(TmpBuf, 0, 0);
3491 else if (errno == EILSEQ){
3492 /* hm, invalid utf8 sequence... what to do now? */
3493 /* An invalid multibyte sequence has been encountered in the input */
3495 else if (errno == EINVAL) {
3496 /* An incomplete multibyte sequence has been encountered in the input. */
3499 FlushStrBuf(TmpBuf);
3502 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3503 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3505 /* little card game: wheres the red lady? */
3506 iSwapBuffers(ConvertBuf, TmpBuf);
3507 FlushStrBuf(TmpBuf);
3514 * catches one RFC822 encoded segment, and decodes it.
3515 * Target buffer to fill with result
3516 * DecodeMe buffer with stuff to process
3517 * SegmentStart points to our current segment in DecodeMe
3518 * SegmentEnd Points to the end of our current segment in DecodeMe
3519 * ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3520 * ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3521 * FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3523 inline static void DecodeSegment(StrBuf *Target,
3524 const StrBuf *DecodeMe,
3525 const char *SegmentStart,
3526 const char *SegmentEnd,
3528 StrBuf *ConvertBuf2,
3529 StrBuf *FoundCharset)
3535 iconv_t ic = (iconv_t)(-1);
3539 /* Now we handle foreign character sets properly encoded
3540 * in RFC2047 format.
3542 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3543 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3544 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3545 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3546 if (FoundCharset != NULL) {
3547 FlushStrBuf(FoundCharset);
3548 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3550 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3551 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3553 *encoding = toupper(*encoding);
3554 if (*encoding == 'B') { /**< base64 */
3555 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3556 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3557 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3559 ConvertBuf->BufUsed);
3561 else if (*encoding == 'Q') { /**< quoted-printable */
3565 while (pos < ConvertBuf->BufUsed)
3567 if (ConvertBuf->buf[pos] == '_')
3568 ConvertBuf->buf[pos] = ' ';
3572 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3573 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3575 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3578 ConvertBuf->BufUsed);
3581 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3584 ctdl_iconv_open("UTF-8", charset, &ic);
3585 if (ic != (iconv_t)(-1) ) {
3587 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3588 StrBufAppendBuf(Target, ConvertBuf2, 0);
3593 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3599 * Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3600 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3601 * Target where to put the decoded string to
3602 * DecodeMe buffer with encoded string
3603 * DefaultCharset if we don't find one, which should we use?
3604 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3605 * put it here for later use where no string might be known.
3607 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3610 StrBuf *ConvertBuf2;
3611 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3612 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3614 StrBuf_RFC822_2_Utf8(Target,
3620 FreeStrBuf(&ConvertBuf);
3621 FreeStrBuf(&ConvertBuf2);
3625 * Handle subjects with RFC2047 encoding such as:
3626 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3627 * Target where to put the decoded string to
3628 * DecodeMe buffer with encoded string
3629 * DefaultCharset if we don't find one, which should we use?
3630 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3631 * put it here for later use where no string might be known.
3632 * ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3633 * ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3635 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3636 const StrBuf *DecodeMe,
3637 const StrBuf* DefaultCharset,
3638 StrBuf *FoundCharset,
3640 StrBuf *ConvertBuf2)
3642 StrBuf *DecodedInvalidBuf = NULL;
3643 const StrBuf *DecodeMee = DecodeMe;
3644 const char *start, *end, *next, *nextend, *ptr = NULL;
3646 iconv_t ic = (iconv_t)(-1) ;
3651 int illegal_non_rfc2047_encoding = 0;
3654 if (DecodeMe == NULL)
3656 /* Sometimes, badly formed messages contain strings which were simply
3657 * written out directly in some foreign character set instead of
3658 * using RFC2047 encoding. This is illegal but we will attempt to
3659 * handle it anyway by converting from a user-specified default
3660 * charset to UTF-8 if we see any nonprintable characters.
3663 for (i=0; i<DecodeMe->BufUsed; ++i) {
3664 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3665 illegal_non_rfc2047_encoding = 1;
3670 if ((illegal_non_rfc2047_encoding) &&
3671 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3672 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3675 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3676 if (ic != (iconv_t)(-1) ) {
3677 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3678 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3679 DecodeMee = DecodedInvalidBuf;
3685 /* pre evaluate the first pair */
3687 start = strstr(DecodeMee->buf, "=?");
3688 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3690 end = FindNextEnd (DecodeMee, start + 2);
3692 StrBufAppendBuf(Target, DecodeMee, 0);
3693 FreeStrBuf(&DecodedInvalidBuf);
3698 if (start != DecodeMee->buf) {
3701 nFront = start - DecodeMee->buf;
3702 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3705 * Since spammers will go to all sorts of absurd lengths to get their
3706 * messages through, there are LOTS of corrupt headers out there.
3707 * So, prevent a really badly formed RFC2047 header from throwing
3708 * this function into an infinite loop.
3710 while ((start != NULL) &&
3717 DecodeSegment(Target,
3725 next = strstr(end, "=?");
3727 if ((next != NULL) &&
3729 nextend = FindNextEnd(DecodeMee, next);
3730 if (nextend == NULL)
3733 /* did we find two partitions */
3734 if ((next != NULL) &&
3738 while ((ptr < next) &&
3745 * did we find a gab just filled with blanks?
3746 * if not, copy its stuff over.
3750 StrBufAppendBufPlain(Target,
3756 /* our next-pair is our new first pair now. */
3762 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3763 if ((end != NULL) && (end < nextend)) {
3765 while ( (ptr < nextend) &&
3772 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3774 FreeStrBuf(&DecodedInvalidBuf);
3777 /*******************************************************************************
3778 * Manipulating UTF-8 Strings *
3779 *******************************************************************************/
3782 * evaluate the length of an utf8 special character sequence
3783 * Char the character to examine
3784 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3786 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3789 unsigned char test = (1<<7);
3791 if ((*CharS & 0xC0) != 0xC0)
3795 ((test & ((unsigned char)*CharS)) != 0))
3800 if ((n > 6) || ((CharE - CharS) < n))
3806 * detect whether this char starts an utf-8 encoded char
3807 * Char character to inspect
3808 * @returns yes or no
3810 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3812 /** 11??.???? indicates an UTF8 Sequence. */
3813 return ((Char & 0xC0) == 0xC0);
3817 * measure the number of glyphs in an UTF8 string...
3818 * Buf string to measure
3819 * @returns the number of glyphs in Buf
3821 long StrBuf_Utf8StrLen(StrBuf *Buf)
3827 if ((Buf == NULL) || (Buf->BufUsed == 0))
3830 eptr = Buf->buf + Buf->BufUsed;
3831 while ((aptr < eptr) && (*aptr != '\0')) {
3832 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3833 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3834 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3846 * cuts a string after maxlen glyphs
3847 * Buf string to cut to maxlen glyphs
3848 * maxlen how long may the string become?
3849 * @returns current length of the string
3851 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3857 eptr = Buf->buf + Buf->BufUsed;
3858 while ((aptr < eptr) && (*aptr != '\0')) {
3859 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3860 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3861 while ((*aptr++ != '\0') && (m-- > 0));
3870 Buf->BufUsed = aptr - Buf->buf;
3871 return Buf->BufUsed;
3874 return Buf->BufUsed;
3882 /*******************************************************************************
3884 *******************************************************************************/
3887 * uses the same calling syntax as compress2(), but it
3888 * creates a stream compatible with HTTP "Content-encoding: gzip"
3889 * dest compressed buffer
3890 * destLen length of the compresed data
3891 * source source to encode
3892 * sourceLen length of source to encode
3893 * level compression level
3896 int ZEXPORT compress_gzip(Bytef * dest,
3898 const Bytef * source,
3902 /* write gzip header */
3903 snprintf((char *) dest, *destLen,
3904 "%c%c%c%c%c%c%c%c%c%c",
3905 gz_magic[0], gz_magic[1], Z_DEFLATED,
3906 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3909 /* normal deflate */
3912 stream.next_in = (Bytef *) source;
3913 stream.avail_in = (uInt) sourceLen;
3914 stream.next_out = dest + 10L; // after header
3915 stream.avail_out = (uInt) * destLen;
3916 if ((uLong) stream.avail_out != *destLen)
3919 stream.zalloc = (alloc_func) 0;
3920 stream.zfree = (free_func) 0;
3921 stream.opaque = (voidpf) 0;
3923 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3924 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3928 err = deflate(&stream, Z_FINISH);
3929 if (err != Z_STREAM_END) {
3930 deflateEnd(&stream);
3931 return err == Z_OK ? Z_BUF_ERROR : err;
3933 *destLen = stream.total_out + 10L;
3935 /* write CRC and Length */
3936 uLong crc = crc32(0L, source, sourceLen);
3938 for (n = 0; n < 4; ++n, ++*destLen) {
3939 dest[*destLen] = (int) (crc & 0xff);
3942 uLong len = stream.total_in;
3943 for (n = 0; n < 4; ++n, ++*destLen) {
3944 dest[*destLen] = (int) (len & 0xff);
3947 err = deflateEnd(&stream);
3954 * compress the buffer with gzip
3955 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3956 * Buf buffer whose content is to be gzipped
3958 int CompressBuffer(StrBuf *Buf)
3961 char *compressed_data = NULL;
3962 size_t compressed_len, bufsize;
3965 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3966 compressed_data = malloc(compressed_len);
3968 if (compressed_data == NULL)
3970 /* Flush some space after the used payload so valgrind shuts up... */
3971 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3972 Buf->buf[Buf->BufUsed + i++] = '\0';
3973 if (compress_gzip((Bytef *) compressed_data,
3976 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3979 Buf->buf = compressed_data;
3980 Buf->BufUsed = compressed_len;
3981 Buf->BufSize = bufsize;
3982 /* Flush some space after the used payload so valgrind shuts up... */
3984 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3985 Buf->buf[Buf->BufUsed + i++] = '\0';
3988 free(compressed_data);
3990 #endif /* HAVE_ZLIB */
3994 /*******************************************************************************
3995 * File I/O; Callbacks to libevent *
3996 *******************************************************************************/
3998 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4003 if ((FB == NULL) || (FB->Buf == NULL))
4007 * check whether the read pointer is somewhere in a range
4008 * where a cut left is inexpensive
4011 if (FB->ReadWritePointer != NULL)
4015 already_read = FB->ReadWritePointer - FB->Buf->buf;
4016 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4018 if (already_read != 0) {
4021 unread = FB->Buf->BufUsed - already_read;
4023 /* else nothing to compact... */
4025 FB->ReadWritePointer = FB->Buf->buf;
4026 bufremain = FB->Buf->BufSize;
4028 else if ((unread < 64) ||
4029 (bufremain < already_read))
4032 * if its just a tiny bit remaining, or we run out of space...
4035 FB->Buf->BufUsed = unread;
4036 if (unread < already_read)
4037 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4039 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4040 FB->ReadWritePointer = FB->Buf->buf;
4041 bufremain = FB->Buf->BufSize - unread - 1;
4043 else if (bufremain < (FB->Buf->BufSize / 10))
4045 /* get a bigger buffer */
4047 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4049 FB->ReadWritePointer = FB->Buf->buf + unread;
4051 bufremain = FB->Buf->BufSize - unread - 1;
4052 /*TODO: special increase function that won't copy the already read! */
4055 else if (bufremain < 10) {
4056 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4058 FB->ReadWritePointer = FB->Buf->buf;
4060 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4065 FB->ReadWritePointer = FB->Buf->buf;
4066 bufremain = FB->Buf->BufSize - 1;
4069 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4072 FB->Buf->BufUsed += n;
4073 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4078 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4083 if ((FB == NULL) || (FB->Buf == NULL))
4086 if (FB->ReadWritePointer != NULL)
4088 WriteRemain = FB->Buf->BufUsed -
4089 (FB->ReadWritePointer -
4093 FB->ReadWritePointer = FB->Buf->buf;
4094 WriteRemain = FB->Buf->BufUsed;
4097 n = write(fd, FB->ReadWritePointer, WriteRemain);
4099 FB->ReadWritePointer += n;
4101 if (FB->ReadWritePointer ==
4102 FB->Buf->buf + FB->Buf->BufUsed)
4104 FlushStrBuf(FB->Buf);
4105 FB->ReadWritePointer = NULL;
4108 // check whether we've got something to write
4109 // get the maximum chunk plus the pointer we can send
4110 // write whats there
4111 // if not all was sent, remember the send pointer for the next time
4112 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4118 * extract a "next line" from Buf; Ptr to persist across several iterations
4119 * LineBuf your line will be copied here.
4120 * FB BLOB with lines of text...
4121 * Ptr moved arround to keep the next-line across several iterations
4122 * has to be &NULL on start; will be &NotNULL on end of buffer
4123 * @returns size of copied buffer
4125 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4127 const char *aptr, *ptr, *eptr;
4130 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4134 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4135 FB->ReadWritePointer = StrBufNOTNULL;
4139 FlushStrBuf(LineBuf);
4140 if (FB->ReadWritePointer == NULL)
4141 ptr = aptr = FB->Buf->buf;
4143 ptr = aptr = FB->ReadWritePointer;
4145 optr = LineBuf->buf;
4146 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4147 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4149 while ((ptr <= eptr) &&
4156 LineBuf->BufUsed = optr - LineBuf->buf;
4157 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4158 optr = LineBuf->buf + LineBuf->BufUsed;
4159 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4164 if (optr > LineBuf->buf)
4166 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4167 LineBuf->BufUsed = optr - LineBuf->buf;
4169 if ((FB->ReadWritePointer != NULL) &&
4170 (FB->ReadWritePointer != FB->Buf->buf))
4172 /* Ok, the client application read all the data
4173 it was interested in so far. Since there is more to read,
4174 we now shrink the buffer, and move the rest over.
4176 StrBufCutLeft(FB->Buf,
4177 FB->ReadWritePointer - FB->Buf->buf);
4178 FB->ReadWritePointer = FB->Buf->buf;
4180 return eMustReadMore;
4183 LineBuf->BufUsed = optr - LineBuf->buf;
4185 if ((ptr <= eptr) && (*ptr == '\r'))
4187 if ((ptr <= eptr) && (*ptr == '\n'))
4191 FB->ReadWritePointer = ptr;
4194 FlushStrBuf(FB->Buf);
4195 FB->ReadWritePointer = NULL;
4198 return eReadSuccess;
4202 * check whether the chunk-buffer has more data waiting or not.
4203 * FB Chunk-Buffer to inspect
4205 eReadState StrBufCheckBuffer(IOBuffer *FB)
4209 if (FB->Buf->BufUsed == 0)
4210 return eReadSuccess;
4211 if (FB->ReadWritePointer == NULL)
4212 return eBufferNotEmpty;
4213 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4214 return eBufferNotEmpty;
4215 return eReadSuccess;
4218 long IOBufferStrLength(IOBuffer *FB)
4220 if ((FB == NULL) || (FB->Buf == NULL))
4222 if (FB->ReadWritePointer == NULL)
4223 return StrLength(FB->Buf);
4225 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4229 /*******************************************************************************
4230 * File I/O; Prefer buffered read since its faster! *
4231 *******************************************************************************/
4234 * Read a line from socket
4235 * flushes and closes the FD on error
4236 * buf the buffer to get the input to
4237 * fd pointer to the filedescriptor to read
4238 * append Append to an existing string or replace?
4239 * Error strerror() on error
4240 * @returns numbers of chars read
4242 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4244 int len, rlen, slen;
4246 if ((buf == NULL) || (buf->buf == NULL)) {
4247 *Error = strerror(EINVAL);
4254 slen = len = buf->BufUsed;
4256 rlen = read(*fd, &buf->buf[len], 1);
4258 *Error = strerror(errno);
4265 if (buf->buf[len] == '\n')
4267 if (buf->buf[len] != '\r')
4269 if (len + 2 >= buf->BufSize) {
4271 buf->buf[len+1] = '\0';
4272 IncreaseBuf(buf, 1, -1);
4276 buf->buf[len] = '\0';
4282 * Read a line from socket
4283 * flushes and closes the FD on error
4284 * Line the line to read from the fd / I/O Buffer
4285 * buf the buffer to get the input to
4286 * fd pointer to the filedescriptor to read
4287 * timeout number of successless selects until we bail out
4288 * selectresolution how long to wait on each select
4289 * Error strerror() on error
4290 * @returns numbers of chars read
4292 int StrBufTCP_read_buffered_line(StrBuf *Line,
4296 int selectresolution,
4300 int nSuccessLess = 0;
4307 if (buf->BufUsed > 0) {
4308 pch = strchr(buf->buf, '\n');
4311 len = pch - buf->buf;
4312 if (len > 0 && (*(pch - 1) == '\r') )
4314 StrBufSub(Line, buf, 0, len - rlen);
4315 StrBufCutLeft(buf, len + 1);
4320 if (buf->BufSize - buf->BufUsed < 10)
4321 IncreaseBuf(buf, 1, -1);
4323 fdflags = fcntl(*fd, F_GETFL);
4324 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4326 while ((nSuccessLess < timeout) && (pch == NULL)) {
4328 tv.tv_sec = selectresolution;
4333 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4334 *Error = strerror(errno);
4340 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4345 &buf->buf[buf->BufUsed],
4346 buf->BufSize - buf->BufUsed - 1);
4348 *Error = strerror(errno);
4353 else if (rlen > 0) {
4355 buf->BufUsed += rlen;
4356 buf->buf[buf->BufUsed] = '\0';
4357 pch = strchr(buf->buf, '\n');
4358 if ((pch == NULL) &&
4359 (buf->BufUsed + 10 > buf->BufSize) &&
4360 (IncreaseBuf(buf, 1, -1) == -1))
4368 len = pch - buf->buf;
4369 if (len > 0 && (*(pch - 1) == '\r') )
4371 StrBufSub(Line, buf, 0, len - rlen);
4372 StrBufCutLeft(buf, len + 1);
4379 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4380 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4381 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4383 * Read a line from socket
4384 * flushes and closes the FD on error
4385 * Line where to append our Line read from the fd / I/O Buffer;
4386 * IOBuf the buffer to get the input to; lifetime pair to FD
4387 * Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4388 * fd pointer to the filedescriptor to read
4389 * timeout number of successless selects until we bail out
4390 * selectresolution how long to wait on each select
4391 * Error strerror() on error
4392 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4394 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4399 int selectresolution,
4402 const char *pche = NULL;
4403 const char *pos = NULL;
4405 int len, rlen, retlen;
4406 int nSuccessLess = 0;
4408 const char *pch = NULL;
4414 if ((Line == NULL) ||
4421 *Error = ErrRBLF_PreConditionFailed;
4426 if ((IOBuf->BufUsed > 0) &&
4428 (pos < IOBuf->buf + IOBuf->BufUsed))
4432 pche = IOBuf->buf + IOBuf->BufUsed;
4436 while ((pch < pche) && (*pch != '\n'))
4438 if (Line->BufUsed + 10 > Line->BufSize)
4441 apos = pcht - Line->buf;
4443 IncreaseBuf(Line, 1, -1);
4444 pcht = Line->buf + apos;
4452 if (len > 0 && (*(pch - 1) == '\r') )
4461 if ((pch >= pche) || (*pch == '\0'))
4469 if ((pch != NULL) &&
4472 if (pch + 1 >= pche) {
4485 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4487 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4488 IncreaseBuf(IOBuf, 1, -1);
4490 fdflags = fcntl(*fd, F_GETFL);
4491 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4494 while ((nSuccessLess < timeout) &&
4504 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4505 *Error = strerror(errno);
4509 *Error = ErrRBLF_SelectFailed;
4512 if (! FD_ISSET(*fd, &rfds) != 0) {
4518 &IOBuf->buf[IOBuf->BufUsed],
4519 IOBuf->BufSize - IOBuf->BufUsed - 1);
4521 *Error = strerror(errno);
4526 else if (rlen > 0) {
4528 pLF = IOBuf->buf + IOBuf->BufUsed;
4529 IOBuf->BufUsed += rlen;
4530 IOBuf->buf[IOBuf->BufUsed] = '\0';
4532 pche = IOBuf->buf + IOBuf->BufUsed;
4534 while ((pLF < pche) && (*pLF != '\n'))
4536 if ((pLF >= pche) || (*pLF == '\0'))
4539 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4543 if (pLF != NULL) apos = pLF - IOBuf->buf;
4544 IncreaseBuf(IOBuf, 1, -1);
4545 if (pLF != NULL) pLF = IOBuf->buf + apos;
4559 if (len > 0 && (*(pLF - 1) == '\r') )
4561 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4562 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4568 return retlen + len;
4570 *Error = ErrRBLF_NotEnoughSentFromServer;
4575 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4577 * Input binary data from socket
4578 * flushes and closes the FD on error
4579 * Buf the buffer to get the input to
4580 * fd pointer to the filedescriptor to read
4581 * append Append to an existing string or replace?
4582 * nBytes the maximal number of bytes to read
4583 * Error strerror() on error
4584 * @returns numbers of chars read
4586 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4597 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4599 *Error = ErrRBLF_BLOBPreConditionFailed;
4604 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4605 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4607 ptr = Buf->buf + Buf->BufUsed;
4609 fdflags = fcntl(*fd, F_GETFL);
4610 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4612 while ((nRead < nBytes) &&
4622 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4623 *Error = strerror(errno);
4627 *Error = ErrRBLF_SelectFailed;
4630 if (! FD_ISSET(*fd, &rfds) != 0) {
4636 if ((rlen = read(*fd,
4638 nBytes - nRead)) == -1) {
4641 *Error = strerror(errno);
4646 Buf->BufUsed += rlen;
4648 Buf->buf[Buf->BufUsed] = '\0';
4652 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4653 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4655 * Input binary data from socket
4656 * flushes and closes the FD on error
4657 * Blob put binary thing here
4658 * IOBuf the buffer to get the input to
4659 * Pos offset inside of IOBuf
4660 * fd pointer to the filedescriptor to read
4661 * append Append to an existing string or replace?
4662 * nBytes the maximal number of bytes to read
4663 * check whether we should search for '000\n' terminators in case of timeouts
4664 * Error strerror() on error
4665 * @returns numbers of chars read
4667 int StrBufReadBLOBBuffered(StrBuf *Blob,
4680 int nAlreadyRead = 0;
4685 int nSuccessLess = 0;
4688 if ((Blob == NULL) ||
4695 *Error = ErrRBB_BLOBFPreConditionFailed;
4701 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4702 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4707 rlen = pos - IOBuf->buf;
4709 rlen = IOBuf->BufUsed - rlen;
4712 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed))
4714 if (rlen < nBytes) {
4715 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4716 Blob->BufUsed += rlen;
4717 Blob->buf[Blob->BufUsed] = '\0';
4718 nAlreadyRead = nRead = rlen;
4721 if (rlen >= nBytes) {
4722 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4723 Blob->BufUsed += nBytes;
4724 Blob->buf[Blob->BufUsed] = '\0';
4725 if (rlen == nBytes) {
4737 if (IOBuf->BufSize < nBytes - nRead) {
4738 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4742 fdflags = fcntl(*fd, F_GETFL);
4743 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4751 while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
4758 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4759 *Error = strerror(errno);
4762 if (*Error == NULL) {
4763 *Error = ErrRBLF_SelectFailed;
4767 if (! FD_ISSET(*fd, &rfds) != 0) {
4772 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
4773 // if (rlen == -1) { 2021feb27 ajc changed this, apparently we will always get at least 1 byte unless the connection is broken
4777 *Error = strerror(errno);
4780 else if (rlen == 0){
4781 if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
4782 StrBufPlain(Blob, HKEY("\n000\n"));
4783 StrBufCutRight(Blob, 5);
4784 return Blob->BufUsed;
4786 else if (!IsNonBlock)
4788 else if (nSuccessLess > MaxTries) {
4790 *Error = ErrRBB_too_many_selects;
4794 else if (rlen > 0) {
4798 IOBuf->BufUsed += rlen;
4801 if (nSuccessLess >= MaxTries) {
4803 *Error = ErrRBB_too_many_selects;
4807 if (nRead > nBytes) {
4808 *Pos = IOBuf->buf + nBytes;
4810 Blob->buf[Blob->BufUsed] = '\0';
4811 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4815 return nRead + nAlreadyRead;
4819 * extract a "next line" from Buf; Ptr to persist across several iterations
4820 * LineBuf your line will be copied here.
4821 * Buf BLOB with lines of text...
4822 * Ptr moved arround to keep the next-line across several iterations
4823 * has to be &NULL on start; will be &NotNULL on end of buffer
4824 * @returns size of remaining buffer
4826 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4828 const char *aptr, *ptr, *eptr;
4831 if ((Buf == NULL) ||
4832 (*Ptr == StrBufNOTNULL) ||
4834 (LineBuf->buf == NULL))
4836 *Ptr = StrBufNOTNULL;
4840 FlushStrBuf(LineBuf);
4842 ptr = aptr = Buf->buf;
4846 optr = LineBuf->buf;
4847 eptr = Buf->buf + Buf->BufUsed;
4848 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4850 while ((ptr <= eptr) &&
4857 LineBuf->BufUsed = optr - LineBuf->buf;
4858 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4859 optr = LineBuf->buf + LineBuf->BufUsed;
4860 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4864 if ((ptr >= eptr) && (optr > LineBuf->buf))
4866 LineBuf->BufUsed = optr - LineBuf->buf;
4868 if ((ptr <= eptr) && (*ptr == '\r'))
4870 if ((ptr <= eptr) && (*ptr == '\n'))
4877 *Ptr = StrBufNOTNULL;
4880 return Buf->BufUsed - (ptr - Buf->buf);
4885 * removes double slashes from pathnames
4886 * Dir directory string to filter
4887 * RemoveTrailingSlash allows / disallows trailing slashes
4889 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4895 while (!IsEmptyStr(a)) {
4907 if ((RemoveTrailingSlash) &&
4913 Dir->BufUsed = b - Dir->buf;
4918 * Decode a quoted-printable encoded StrBuf buffer "in place"
4919 * This is possible because the decoded will always be shorter than the encoded
4920 * so we don't have to worry about the buffer being to small.
4922 void StrBufDecodeQP(StrBuf *Buf)
4924 if (!Buf) { // sanity check #1
4928 int source_len = StrLength(Buf);
4929 if (source_len < 1) { // sanity check #2
4933 int spos = 0; // source position
4934 int tpos = 0; // target position
4936 while (spos < source_len) {
4937 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
4940 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
4943 else if (Buf->buf[spos] == '=') {
4946 sscanf(&Buf->buf[spos], "%02x", &ch);
4947 Buf->buf[tpos++] = ch;
4951 Buf->buf[tpos++] = Buf->buf[spos++];
4956 Buf->BufUsed = tpos;