1 // Copyright (c) 1987-2022 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,
37 const Bytef * source, uLong sourceLen, int level);
39 int BaseStrBufSize = 64;
41 int ZLibCompressionRatio = -1; /* defaults to 6 */
43 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
44 #define OS_CODE 0x03 /*< unix */
45 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
48 const char *StrBufNOTNULL = ((char*) NULL) - 1;
50 const char HexList[256][3] = {
51 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
52 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
53 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
54 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
55 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
56 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
57 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
58 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
59 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
60 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
61 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
62 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
63 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
64 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
65 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
66 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
70 * Private Structure for the Stringbuffer
73 char *buf; /**< the pointer to the dynamic buffer */
74 long BufSize; /**< how many spcae do we optain */
75 long BufUsed; /**< StNumber of Chars used excluding the trailing \\0 */
76 int ConstBuf; /**< are we just a wrapper arround a static buffer and musn't we be changed? */
78 long nIncreases; /**< for profiling; cound how many times we needed more */
79 char bt [SIZ]; /**< Stacktrace of last increase */
80 char bt_lastinc [SIZ]; /**< How much did we increase last time? */
85 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
86 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
90 static void StrBufBacktrace(StrBuf *Buf, int which) {
93 void *stack_frames[50];
98 pstart = pch = Buf->bt;
100 pstart = pch = Buf->bt_lastinc;
101 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
102 strings = backtrace_symbols(stack_frames, size);
103 for (i = 0; i < size; i++) {
105 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
107 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
117 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere) {
118 if (hFreeDbglog == -1) {
119 pid_t pid = getpid();
121 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
122 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
124 if ((*FreeMe)->nIncreases > 0) {
127 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
129 (*FreeMe)->nIncreases,
133 (*FreeMe)->bt_lastinc);
134 n = write(hFreeDbglog, buf, n);
139 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
143 n = write(hFreeDbglog, buf, n);
148 void dbg_IncreaseBuf(StrBuf *IncMe) {
150 #ifdef HAVE_BACKTRACE
151 StrBufBacktrace(Buf, 1);
156 void dbg_Init(StrBuf *Buf) {
159 Buf->bt_lastinc[0] = '\0';
160 #ifdef HAVE_BACKTRACE
161 StrBufBacktrace(Buf, 0);
167 #define dbg_FreeStrBuf(a, b)
168 #define dbg_IncreaseBuf(a)
174 * swaps the contents of two StrBufs
175 * this is to be used to have cheap switched between a work-buffer and a target buffer
179 static inline void iSwapBuffers(StrBuf *A, StrBuf *B) {
182 memcpy(&C, A, sizeof(*A));
183 memcpy(A, B, sizeof(*B));
184 memcpy(B, &C, sizeof(C));
189 void SwapBuffers(StrBuf *A, StrBuf *B) {
195 * Cast operator to Plain String
196 * @note if the buffer is altered by StrBuf operations, this pointer may become
197 * invalid. So don't lean on it after altering the buffer!
198 * Since this operation is considered cheap, rather call it often than risking
199 * your pointer to become invalid!
200 * Str the string we want to get the c-string representation for
201 * @returns the Pointer to the Content. Don't mess with it!
203 inline const char *ChrPtr(const StrBuf *Str) {
211 * since we know strlen()'s result, provide it here.
212 * Str the string to return the length to
213 * @returns contentlength of the buffer
215 inline int StrLength(const StrBuf *Str) {
216 return (Str != NULL) ? Str->BufUsed : 0;
220 // local utility function to resize the buffer
221 // Buf the buffer whichs storage we should increase
222 // KeepOriginal should we copy the original buffer or just start over with a new one
223 // DestSize what should fit in after?
224 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
226 size_t NewSize = Buf->BufSize * 2;
233 while ((NewSize <= DestSize) && (NewSize != 0)) {
242 NewBuf = (char*) malloc(NewSize);
243 if (NewBuf == NULL) {
247 if (KeepOriginal && (Buf->BufUsed > 0)) {
248 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
256 Buf->BufSize = NewSize;
258 dbg_IncreaseBuf(Buf);
264 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
265 // Buf Buffer to shrink (has to be empty)
266 // ThreshHold if the buffer is bigger then this, its readjusted
267 // NewSize if we Shrink it, how big are we going to be afterwards?
268 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
269 if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
271 Buf->buf = (char*) malloc(NewSize);
273 Buf->BufSize = NewSize;
279 * shrink long term buffers to their real size so they don't waste memory
280 * Buf buffer to shrink
281 * Force if not set, will just executed if the buffer is much to big; set for lifetime strings
282 * @returns physical size of the buffer
284 long StrBufShrinkToFit(StrBuf *Buf, int Force) {
287 if (Force || (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize)) {
290 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
294 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
295 Buf->BufSize = Buf->BufUsed + 1;
304 * Allocate a new buffer with default buffer size
305 * @returns the new stringbuffer
307 StrBuf *NewStrBuf(void) {
310 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
314 NewBuf->buf = (char*) malloc(BaseStrBufSize);
315 if (NewBuf->buf == NULL) {
319 NewBuf->buf[0] = '\0';
320 NewBuf->BufSize = BaseStrBufSize;
322 NewBuf->ConstBuf = 0;
330 * Copy Constructor; returns a duplicate of CopyMe
331 * CopyMe Buffer to faxmilate
332 * @returns the new stringbuffer
334 StrBuf *NewStrBufDup(const StrBuf *CopyMe) {
340 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
344 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
345 if (NewBuf->buf == NULL) {
350 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
351 NewBuf->BufUsed = CopyMe->BufUsed;
352 NewBuf->BufSize = CopyMe->BufSize;
353 NewBuf->ConstBuf = 0;
362 * Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
363 * NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
364 * CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
365 * CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
366 * KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
367 * @returns the new stringbuffer
369 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal) {
372 if (CreateRelpaceMe == NULL)
376 if (*CreateRelpaceMe != NULL)
377 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
379 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
383 if (CopyFlushMe == NULL) {
384 if (*CreateRelpaceMe != NULL)
385 FlushStrBuf(*CreateRelpaceMe);
387 *CreateRelpaceMe = NewStrBuf();
392 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
393 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
394 * be a big IO-Buffer...
396 if (KeepOriginal || (StrLength(CopyFlushMe) < 256)) {
397 if (*CreateRelpaceMe == NULL) {
398 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
402 NewBuf = *CreateRelpaceMe;
405 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
408 if (*CreateRelpaceMe == NULL) {
409 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
413 NewBuf = *CreateRelpaceMe;
415 iSwapBuffers (NewBuf, CopyFlushMe);
418 FlushStrBuf(CopyFlushMe);
425 * create a new Buffer using an existing c-string
426 * this function should also be used if you want to pre-suggest
427 * the buffer size to allocate in conjunction with ptr == NULL
428 * ptr the c-string to copy; may be NULL to create a blank instance
429 * nChars How many chars should we copy; -1 if we should measure the length ourselves
430 * @returns the new stringbuffer
432 StrBuf *NewStrBufPlain(const char* ptr, int nChars) {
434 size_t Siz = BaseStrBufSize;
437 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
442 CopySize = strlen((ptr != NULL)?ptr:"");
446 while ((Siz <= CopySize) && (Siz != 0))
454 NewBuf->buf = (char*) malloc(Siz);
455 if (NewBuf->buf == NULL) {
459 NewBuf->BufSize = Siz;
461 memcpy(NewBuf->buf, ptr, CopySize);
462 NewBuf->buf[CopySize] = '\0';
463 NewBuf->BufUsed = CopySize;
466 NewBuf->buf[0] = '\0';
469 NewBuf->ConstBuf = 0;
478 * Set an existing buffer from a c-string
480 * ptr c-string to put into
481 * nChars set to -1 if we should work 0-terminated
482 * @returns the new length of the string
484 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars) {
498 CopySize = strlen(ptr);
502 while ((Siz <= CopySize) && (Siz != 0))
510 if (Siz != Buf->BufSize)
511 IncreaseBuf(Buf, 0, Siz);
512 memcpy(Buf->buf, ptr, CopySize);
513 Buf->buf[CopySize] = '\0';
514 Buf->BufUsed = CopySize;
521 * use strbuf as wrapper for a string constant for easy handling
522 * StringConstant a string to wrap
523 * SizeOfStrConstant should be sizeof(StringConstant)-1
525 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
529 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
532 NewBuf->buf = (char*) StringConstant;
533 NewBuf->BufSize = SizeOfStrConstant;
534 NewBuf->BufUsed = SizeOfStrConstant;
535 NewBuf->ConstBuf = 1;
544 * flush the content of a Buf; keep its struct
545 * buf Buffer to flush
547 int FlushStrBuf(StrBuf *buf)
549 if ((buf == NULL) || (buf->buf == NULL))
559 * wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
562 int FLUSHStrBuf(StrBuf *buf)
568 if (buf->BufUsed > 0) {
569 memset(buf->buf, 0, buf->BufUsed);
576 int hFreeDbglog = -1;
580 * Its a double pointer, so it can NULL your pointer
581 * so fancy SIG11 appear instead of random results
582 * FreeMe Pointer Pointer to the buffer to free
584 void FreeStrBuf (StrBuf **FreeMe)
589 dbg_FreeStrBuf(FreeMe, 'F');
591 if (!(*FreeMe)->ConstBuf)
592 free((*FreeMe)->buf);
598 * flatten a Buffer to the Char * we return
599 * Its a double pointer, so it can NULL your pointer
600 * so fancy SIG11 appear instead of random results
601 * The Callee then owns the buffer and is responsible for freeing it.
602 * SmashMe Pointer Pointer to the buffer to release Buf from and free
603 * @returns the pointer of the buffer; Callee owns the memory thereafter.
605 char *SmashStrBuf (StrBuf **SmashMe) {
608 if ((SmashMe == NULL) || (*SmashMe == NULL))
611 dbg_FreeStrBuf(SmashMe, 'S');
613 Ret = (*SmashMe)->buf;
622 * If you want put your StrBuf into a Hash, use this as Destructor.
623 * VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
625 void HFreeStrBuf (void *VFreeMe) {
626 StrBuf *FreeMe = (StrBuf*)VFreeMe;
630 dbg_FreeStrBuf(SmashMe, 'H');
632 if (!FreeMe->ConstBuf)
638 /*******************************************************************************
639 * Simple string transformations *
640 *******************************************************************************/
643 * Wrapper around atol
645 long StrTol(const StrBuf *Buf)
650 return atol(Buf->buf);
656 * Wrapper around atoi
658 int StrToi(const StrBuf *Buf)
662 if (Buf->BufUsed > 0)
663 return atoi(Buf->buf);
669 * Checks to see if the string is a pure number
670 * Buf The buffer to inspect
671 * @returns 1 if its a pure number, 0, if not.
673 int StrBufIsNumber(const StrBuf *Buf) {
675 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
678 strtoll(Buf->buf, &pEnd, 10);
679 if (pEnd == Buf->buf)
681 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
683 if (Buf->buf == pEnd)
689 * modifies a Single char of the Buf
690 * You can point to it via char* or a zero-based integer
691 * Buf The buffer to manipulate
692 * ptr char* to zero; use NULL if unused
693 * nThChar zero based pointer into the string; use -1 if unused
694 * PeekValue The Character to place into the position
696 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
701 nThChar = ptr - Buf->buf;
702 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
704 Buf->buf[nThChar] = PeekValue;
709 * modifies a range of chars of the Buf
710 * You can point to it via char* or a zero-based integer
711 * Buf The buffer to manipulate
712 * ptr char* to zero; use NULL if unused
713 * nThChar zero based pointer into the string; use -1 if unused
714 * nChars how many chars are to be flushed?
715 * PookValue The Character to place into that area
717 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
722 nThChar = ptr - Buf->buf;
723 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
725 if (nThChar + nChars > Buf->BufUsed)
726 nChars = Buf->BufUsed - nThChar;
728 memset(Buf->buf + nThChar, PookValue, nChars);
729 /* just to be shure... */
730 Buf->buf[Buf->BufUsed] = 0;
735 * Append a StringBuffer to the buffer
736 * Buf Buffer to modify
737 * AppendBuf Buffer to copy at the end of our buffer
738 * Offset Should we start copying from an offset?
740 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
742 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
743 (Buf == NULL) || (Buf->buf == NULL))
746 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
747 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
749 memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
750 Buf->BufUsed += AppendBuf->BufUsed - Offset;
751 Buf->buf[Buf->BufUsed] = '\0';
755 // Append a C-String to the buffer
756 // Buf Buffer to modify
757 // AppendBuf Buffer to copy at the end of our buffer
758 // AppendSize number of bytes to copy; set to -1 if we should count it in advance
759 // Offset Should we start copying from an offset?
760 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
762 long BufSizeRequired;
764 if ((AppendBuf == NULL) || (Buf == NULL))
767 if (AppendSize < 0) {
768 aps = strlen(AppendBuf + Offset);
771 aps = AppendSize - Offset;
774 BufSizeRequired = Buf->BufUsed + aps + 1;
775 if (Buf->BufSize <= BufSizeRequired) {
776 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
779 memcpy(Buf->buf + Buf->BufUsed,
783 Buf->buf[Buf->BufUsed] = '\0';
787 * sprintf like function appending the formated string to the buffer
788 * vsnprintf version to wrap into own calls
789 * Buf Buffer to extend by format and Params
790 * format printf alike format to add
791 * ap va_list containing the items for format
793 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
801 if ((Buf == NULL) || (format == NULL))
804 BufSize = Buf->BufSize;
805 nWritten = Buf->BufSize + 1;
806 Offset = Buf->BufUsed;
807 newused = Offset + nWritten;
809 while (newused >= BufSize) {
811 nWritten = vsnprintf(Buf->buf + Offset,
812 Buf->BufSize - Offset,
815 newused = Offset + nWritten;
816 if (newused >= Buf->BufSize) {
817 if (IncreaseBuf(Buf, 1, newused) == -1)
818 return; /* TODO: error handling? */
819 newused = Buf->BufSize + 1;
822 Buf->BufUsed = Offset + nWritten;
823 BufSize = Buf->BufSize;
830 * sprintf like function appending the formated string to the buffer
831 * Buf Buffer to extend by format and Params
832 * format printf alike format to add
834 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
842 if ((Buf == NULL) || (format == NULL))
845 BufSize = Buf->BufSize;
846 nWritten = Buf->BufSize + 1;
847 Offset = Buf->BufUsed;
848 newused = Offset + nWritten;
850 while (newused >= BufSize) {
851 va_start(arg_ptr, format);
852 nWritten = vsnprintf(Buf->buf + Buf->BufUsed,
853 Buf->BufSize - Buf->BufUsed,
856 newused = Buf->BufUsed + nWritten;
857 if (newused >= Buf->BufSize) {
858 if (IncreaseBuf(Buf, 1, newused) == -1)
859 return; /* TODO: error handling? */
860 newused = Buf->BufSize + 1;
863 Buf->BufUsed += nWritten;
864 BufSize = Buf->BufSize;
871 * sprintf like function putting the formated string into the buffer
872 * Buf Buffer to extend by format and Parameters
873 * format printf alike format to add
875 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
880 if ((Buf == NULL) || (format == NULL))
883 nWritten = Buf->BufSize + 1;
884 while (nWritten >= Buf->BufSize) {
885 va_start(arg_ptr, format);
886 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
888 if (nWritten >= Buf->BufSize) {
889 if (IncreaseBuf(Buf, 0, 0) == -1)
890 return; /* TODO: error handling? */
891 nWritten = Buf->BufSize + 1;
894 Buf->BufUsed = nWritten ;
899 * Callback for cURL to append the webserver reply to a buffer
900 * ptr pre-defined by the cURL API; see man 3 curl for mre info
901 * size pre-defined by the cURL API; see man 3 curl for mre info
902 * nmemb pre-defined by the cURL API; see man 3 curl for mre info
903 * stream pre-defined by the cURL API; see man 3 curl for mre info
905 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
914 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
920 * extracts a substring from Source into dest
921 * dest buffer to place substring into
922 * Source string to copy substring from
923 * Offset chars to skip from start
924 * nChars number of chars to copy
925 * @returns the number of chars copied; may be different from nChars due to the size of Source
927 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
930 if (Offset > Source->BufUsed)
936 if (Offset + nChars < Source->BufUsed)
938 if ((nChars >= dest->BufSize) &&
939 (IncreaseBuf(dest, 0, nChars + 1) == -1))
941 memcpy(dest->buf, Source->buf + Offset, nChars);
942 dest->BufUsed = nChars;
943 dest->buf[dest->BufUsed] = '\0';
946 NCharsRemain = Source->BufUsed - Offset;
947 if ((NCharsRemain >= dest->BufSize) &&
948 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
950 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
951 dest->BufUsed = NCharsRemain;
952 dest->buf[dest->BufUsed] = '\0';
957 * Cut nChars from the start of the string
958 * Buf Buffer to modify
959 * nChars how many chars should be skipped?
961 void StrBufCutLeft(StrBuf *Buf, int nChars)
963 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
964 if (nChars >= Buf->BufUsed) {
968 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
969 Buf->BufUsed -= nChars;
970 Buf->buf[Buf->BufUsed] = '\0';
974 * Cut the trailing n Chars from the string
975 * Buf Buffer to modify
976 * nChars how many chars should be trunkated?
978 void StrBufCutRight(StrBuf *Buf, int nChars)
980 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
983 if (nChars >= Buf->BufUsed) {
987 Buf->BufUsed -= nChars;
988 Buf->buf[Buf->BufUsed] = '\0';
992 * Cut the string after n Chars
993 * Buf Buffer to modify
994 * AfternChars after how many chars should we trunkate the string?
995 * At if non-null and points inside of our string, cut it there.
997 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
999 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1001 AfternChars = At - Buf->buf;
1004 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1006 Buf->BufUsed = AfternChars;
1007 Buf->buf[Buf->BufUsed] = '\0';
1012 * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1013 * Buf the string to modify
1015 void StrBufTrim(StrBuf *Buf)
1018 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1020 while ((Buf->BufUsed > 0) &&
1021 isspace(Buf->buf[Buf->BufUsed - 1]))
1025 Buf->buf[Buf->BufUsed] = '\0';
1027 if (Buf->BufUsed == 0) return;
1029 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1032 if (delta > 0) StrBufCutLeft(Buf, delta);
1035 * changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1036 * Buf the string to modify
1038 void StrBufSpaceToBlank(StrBuf *Buf)
1042 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1045 pche = pch + Buf->BufUsed;
1054 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1059 if ((Buf == NULL) || (Buf->buf == NULL)) {
1063 pRight = strchr(Buf->buf, rightboundary);
1064 if (pRight != NULL) {
1065 StrBufCutAt(Buf, 0, pRight);
1068 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1069 if (pLeft != NULL) {
1070 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1076 * uppercase the contents of a buffer
1077 * Buf the buffer to translate
1079 void StrBufUpCase(StrBuf *Buf)
1083 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1086 pche = pch + Buf->BufUsed;
1087 while (pch < pche) {
1088 *pch = toupper(*pch);
1095 * lowercase the contents of a buffer
1096 * Buf the buffer to translate
1098 void StrBufLowerCase(StrBuf *Buf)
1102 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1105 pche = pch + Buf->BufUsed;
1106 while (pch < pche) {
1107 *pch = tolower(*pch);
1113 /*******************************************************************************
1114 * a tokenizer that kills, maims, and destroys *
1115 *******************************************************************************/
1118 * Replace a token at a given place with a given length by another token with given length
1119 * Buf String where to work on
1120 * where where inside of the Buf is the search-token
1121 * HowLong How long is the token to be replaced
1122 * Repl Token to insert at 'where'
1123 * ReplLen Length of repl
1124 * @returns -1 if fail else length of resulting Buf
1126 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1127 const char *Repl, long ReplLen)
1130 if ((Buf == NULL) ||
1131 (where > Buf->BufUsed) ||
1132 (where + HowLong > Buf->BufUsed))
1135 if (where + ReplLen - HowLong > Buf->BufSize)
1136 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1139 memmove(Buf->buf + where + ReplLen,
1140 Buf->buf + where + HowLong,
1141 Buf->BufUsed - where - HowLong);
1143 memcpy(Buf->buf + where,
1146 Buf->BufUsed += ReplLen - HowLong;
1148 return Buf->BufUsed;
1152 * Counts the numbmer of tokens in a buffer
1153 * source String to count tokens in
1154 * tok Tokenizer char to count
1155 * @returns numbers of tokenizer chars found
1157 int StrBufNum_tokens(const StrBuf *source, char tok)
1161 if ((source == NULL) || (source->BufUsed == 0))
1163 if ((source->BufUsed == 1) && (*source->buf == tok))
1167 pche = pch + source->BufUsed;
1178 * a string tokenizer
1179 * Source StringBuffer to read into
1180 * parmnum n'th Parameter to remove
1181 * separator tokenizer character
1182 * @returns -1 if not found, else length of token.
1184 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1187 char *d, *s, *end; /* dest, source */
1190 /* Find desired eter */
1191 end = Source->buf + Source->BufUsed;
1193 while ((d <= end) &&
1196 /* End of string, bail! */
1201 if (*d == separator) {
1206 if ((d == NULL) || (d >= end))
1207 return 0; /* @Parameter not found */
1209 /* Find next eter */
1211 while ((s <= end) &&
1212 (*s && *s != separator))
1216 if (*s == separator)
1220 /* Hack and slash */
1225 memmove(d, s, Source->BufUsed - (s - Source->buf));
1226 Source->BufUsed += ReducedBy;
1227 Source->buf[Source->BufUsed] = '\0';
1229 else if (d == Source->buf) {
1231 Source->BufUsed = 0;
1235 Source->BufUsed += ReducedBy;
1246 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1248 const StrBuf Temp = {
1261 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1265 * a string tokenizer
1266 * dest Destination StringBuffer
1267 * Source StringBuffer to read into
1268 * parmnum n'th Parameter to extract
1269 * separator tokenizer character
1270 * @returns -1 if not found, else length of token.
1272 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1274 const char *s, *e; //* source * /
1275 int len = 0; //* running total length of extracted string * /
1276 int current_token = 0; //* token currently being processed * /
1279 dest->buf[0] = '\0';
1285 if ((Source == NULL) || (Source->BufUsed ==0)) {
1289 e = s + Source->BufUsed;
1292 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1294 while ((s < e) && !IsEmptyStr(s)) {
1295 if (*s == separator) {
1298 if (len >= dest->BufSize) {
1299 dest->BufUsed = len;
1300 if (IncreaseBuf(dest, 1, -1) < 0) {
1305 if ( (current_token == parmnum) &&
1306 (*s != separator)) {
1307 dest->buf[len] = *s;
1310 else if (current_token > parmnum) {
1316 dest->buf[len] = '\0';
1317 dest->BufUsed = len;
1319 if (current_token < parmnum) {
1320 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1323 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1332 * a string tokenizer to fetch an integer
1333 * Source String containing tokens
1334 * parmnum n'th Parameter to extract
1335 * separator tokenizer character
1336 * @returns 0 if not found, else integer representation of the token
1338 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1348 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1355 * a string tokenizer to fetch a long integer
1356 * Source String containing tokens
1357 * parmnum n'th Parameter to extract
1358 * separator tokenizer character
1359 * @returns 0 if not found, else long integer representation of the token
1361 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1371 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1379 * a string tokenizer to fetch an unsigned long
1380 * Source String containing tokens
1381 * parmnum n'th Parameter to extract
1382 * separator tokenizer character
1383 * @returns 0 if not found, else unsigned long representation of the token
1385 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1396 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1400 return (unsigned long) atol(pnum);
1409 * a string tokenizer; Bounds checker
1410 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1411 * Source our tokenbuffer
1412 * pStart the token iterator pointer to inspect
1413 * @returns whether the revolving pointer is inside of the search range
1415 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1417 if ((Source == NULL) ||
1418 (*pStart == StrBufNOTNULL) ||
1419 (Source->BufUsed == 0))
1423 if (*pStart == NULL)
1427 else if (*pStart > Source->buf + Source->BufUsed)
1431 else if (*pStart <= Source->buf)
1440 * a string tokenizer
1441 * dest Destination StringBuffer
1442 * Source StringBuffer to read into
1443 * pStart pointer to the end of the last token. Feed with NULL on start.
1444 * separator tokenizer
1445 * @returns -1 if not found, else length of token.
1447 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1449 const char *s; /* source */
1450 const char *EndBuffer; /* end stop of source buffer */
1451 int current_token = 0; /* token currently being processed */
1452 int len = 0; /* running total length of extracted string */
1454 if ((Source == NULL) ||
1455 (Source->BufUsed == 0) )
1457 *pStart = StrBufNOTNULL;
1463 EndBuffer = Source->buf + Source->BufUsed;
1467 dest->buf[0] = '\0';
1472 *pStart = EndBuffer + 1;
1476 if (*pStart == NULL)
1478 *pStart = Source->buf; /* we're starting to examine this buffer. */
1480 else if ((*pStart < Source->buf) ||
1481 (*pStart > EndBuffer ) )
1483 return -1; /* no more tokens to find. */
1487 /* start to find the next token */
1488 while ((s <= EndBuffer) &&
1489 (current_token == 0) )
1491 if (*s == separator)
1493 /* we found the next token */
1497 if (len >= dest->BufSize)
1499 /* our Dest-buffer isn't big enough, increase it. */
1500 dest->BufUsed = len;
1502 if (IncreaseBuf(dest, 1, -1) < 0) {
1503 /* WHUT? no more mem? bail out. */
1510 if ( (current_token == 0 ) && /* are we in our target token? */
1511 (!IsEmptyStr(s) ) &&
1512 (separator != *s) ) /* don't copy the token itself */
1514 dest->buf[len] = *s; /* Copy the payload */
1515 ++len; /* remember the bigger size. */
1521 /* did we reach the end? */
1522 if ((s > EndBuffer)) {
1523 EndBuffer = StrBufNOTNULL;
1524 *pStart = EndBuffer;
1527 *pStart = s; /* remember the position for the next run */
1530 /* sanitize our extracted token */
1531 dest->buf[len] = '\0';
1532 dest->BufUsed = len;
1539 * a string tokenizer
1540 * Source StringBuffer to read from
1541 * pStart pointer to the end of the last token. Feed with NULL.
1542 * separator tokenizer character
1543 * nTokens number of tokens to fastforward over
1544 * @returns -1 if not found, else length of token.
1546 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1548 const char *s, *EndBuffer; //* source * /
1549 int len = 0; //* running total length of extracted string * /
1550 int current_token = 0; //* token currently being processed * /
1552 if ((Source == NULL) ||
1553 (Source->BufUsed ==0)) {
1557 return Source->BufUsed;
1559 if (*pStart == NULL)
1560 *pStart = Source->buf;
1562 EndBuffer = Source->buf + Source->BufUsed;
1564 if ((*pStart < Source->buf) ||
1565 (*pStart > EndBuffer)) {
1573 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1575 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1576 if (*s == separator) {
1579 if (current_token >= nTokens) {
1591 * a string tokenizer to fetch an integer
1592 * Source StringBuffer to read from
1593 * pStart Cursor on the tokenstring
1594 * separator tokenizer character
1595 * @returns 0 if not found, else integer representation of the token
1597 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1607 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1614 * a string tokenizer to fetch a long integer
1615 * Source StringBuffer to read from
1616 * pStart Cursor on the tokenstring
1617 * separator tokenizer character
1618 * @returns 0 if not found, else long integer representation of the token
1620 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1630 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1638 * a string tokenizer to fetch an unsigned long
1639 * Source StringBuffer to read from
1640 * pStart Cursor on the tokenstring
1641 * separator tokenizer character
1642 * @returns 0 if not found, else unsigned long representation of the token
1644 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1655 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1659 return (unsigned long) atol(pnum);
1669 /*******************************************************************************
1670 * Escape Appending *
1671 *******************************************************************************/
1674 * Escape a string for feeding out as a URL while appending it to a Buffer
1675 * OutBuf the output buffer
1676 * In Buffer to encode
1677 * PlainIn way in from plain old c strings
1679 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1681 const char *pch, *pche;
1685 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1687 if (PlainIn != NULL) {
1688 len = strlen(PlainIn);
1694 pche = pch + In->BufUsed;
1701 pt = OutBuf->buf + OutBuf->BufUsed;
1702 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1704 while (pch < pche) {
1706 IncreaseBuf(OutBuf, 1, -1);
1707 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1708 pt = OutBuf->buf + OutBuf->BufUsed;
1711 if((*pch >= 'a' && *pch <= 'z') ||
1712 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1713 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1714 (*pch == '!') || (*pch == '_') ||
1715 (*pch == ',') || (*pch == '.'))
1722 *(pt + 1) = HexList[(unsigned char)*pch][0];
1723 *(pt + 2) = HexList[(unsigned char)*pch][1];
1725 OutBuf->BufUsed += 3;
1734 * Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1735 * OutBuf the output buffer
1736 * In Buffer to encode
1737 * PlainIn way in from plain old c strings
1739 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1740 const char *pch, *pche;
1744 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1746 if (PlainIn != NULL) {
1747 len = strlen(PlainIn);
1753 pche = pch + In->BufUsed;
1760 pt = OutBuf->buf + OutBuf->BufUsed;
1761 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1763 while (pch < pche) {
1765 IncreaseBuf(OutBuf, 1, -1);
1766 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1767 pt = OutBuf->buf + OutBuf->BufUsed;
1770 if((*pch >= 'a' && *pch <= 'z') ||
1771 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1772 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1773 (*pch == '!') || (*pch == '_') ||
1774 (*pch == ',') || (*pch == '.'))
1781 *(pt + 1) = HexList[(unsigned char)*pch][0];
1782 *(pt + 2) = HexList[(unsigned char)*pch][1];
1784 OutBuf->BufUsed += 3;
1792 * append a string with characters having a special meaning in xml encoded to the buffer
1793 * OutBuf the output buffer
1794 * In Buffer to encode
1795 * PlainIn way in from plain old c strings
1796 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1797 * OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1799 void StrBufXMLEscAppend(StrBuf *OutBuf,
1801 const char *PlainIn,
1803 int OverrideLowChars)
1805 const char *pch, *pche;
1810 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1812 if (PlainIn != NULL) {
1814 len = strlen((const char*)PlainIn);
1821 pch = (const char*)In->buf;
1822 pche = pch + In->BufUsed;
1829 pt = OutBuf->buf + OutBuf->BufUsed;
1830 /**< we max append 6 chars at once plus the \0 */
1831 pte = OutBuf->buf + OutBuf->BufSize - 6;
1833 while (pch < pche) {
1835 OutBuf->BufUsed = pt - OutBuf->buf;
1836 IncreaseBuf(OutBuf, 1, -1);
1837 pte = OutBuf->buf + OutBuf->BufSize - 6;
1838 /**< we max append 3 chars at once plus the \0 */
1840 pt = OutBuf->buf + OutBuf->BufUsed;
1844 memcpy(pt, HKEY("<"));
1848 else if (*pch == '>') {
1849 memcpy(pt, HKEY(">"));
1853 else if (*pch == '&') {
1854 memcpy(pt, HKEY("&"));
1858 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
1863 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
1866 while ((IsUtf8Sequence > 0) &&
1879 *pt = HexList[*(unsigned char*)pch][0];
1881 *pt = HexList[*(unsigned char*)pch][1];
1890 OutBuf->BufUsed = pt - OutBuf->buf;
1895 * append a string in hex encoding to the buffer
1896 * OutBuf the output buffer
1897 * In Buffer to encode
1898 * PlainIn way in from plain old c strings
1899 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1901 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1903 const unsigned char *pch, *pche;
1907 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1909 if (PlainIn != NULL) {
1911 len = strlen((const char*)PlainIn);
1918 pch = (const unsigned char*)In->buf;
1919 pche = pch + In->BufUsed;
1926 pt = OutBuf->buf + OutBuf->BufUsed;
1927 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1929 while (pch < pche) {
1931 IncreaseBuf(OutBuf, 1, -1);
1932 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1933 pt = OutBuf->buf + OutBuf->BufUsed;
1936 *pt = HexList[*pch][0];
1938 *pt = HexList[*pch][1];
1939 pt ++; pch ++; OutBuf->BufUsed += 2;
1945 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks) {
1951 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1953 if (PlainIn != NULL) {
1955 len = strlen(PlainIn);
1968 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
1970 if (ExpectLen > OutBuf->BufSize)
1971 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
1974 pt = OutBuf->buf + OutBuf->BufUsed;
1976 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
1979 OutBuf->BufUsed += len;
1984 // append a string in hex encoding to the buffer
1985 // OutBuf the output buffer
1986 // In Buffer to encode
1987 // PlainIn way in from plain old c strings
1988 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1989 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1993 * Append a string, escaping characters which have meaning in HTML.
1995 * Target target buffer
1996 * Source source buffer; set to NULL if you just have a C-String
1997 * PlainIn Plain-C string to append; set to NULL if unused
1998 * nbsp If nonzero, spaces are converted to non-breaking spaces.
1999 * nolinebreaks if set to 1, linebreaks are removed from the string.
2000 * if set to 2, linebreaks are replaced by <br/>
2002 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2004 const char *aptr, *eiptr;
2008 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2011 if (PlainIn != NULL) {
2013 len = strlen(PlainIn);
2018 eiptr = aptr + Source->BufUsed;
2019 len = Source->BufUsed;
2025 bptr = Target->buf + Target->BufUsed;
2026 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2028 while (aptr < eiptr){
2030 IncreaseBuf(Target, 1, -1);
2031 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2032 bptr = Target->buf + Target->BufUsed;
2035 memcpy(bptr, "<", 4);
2037 Target->BufUsed += 4;
2039 else if (*aptr == '>') {
2040 memcpy(bptr, ">", 4);
2042 Target->BufUsed += 4;
2044 else if (*aptr == '&') {
2045 memcpy(bptr, "&", 5);
2047 Target->BufUsed += 5;
2049 else if (*aptr == '"') {
2050 memcpy(bptr, """, 6);
2052 Target->BufUsed += 6;
2054 else if (*aptr == '\'') {
2055 memcpy(bptr, "'", 5);
2057 Target->BufUsed += 5;
2059 else if (*aptr == LB) {
2064 else if (*aptr == RB) {
2069 else if (*aptr == QU) {
2074 else if ((*aptr == 32) && (nbsp == 1)) {
2075 memcpy(bptr, " ", 6);
2077 Target->BufUsed += 6;
2079 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2080 *bptr='\0'; /* nothing */
2082 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2083 memcpy(bptr, "<br/>", 11);
2085 Target->BufUsed += 11;
2089 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2090 *bptr='\0'; /* nothing */
2100 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2102 return Target->BufUsed;
2106 * Append a string, escaping characters which have meaning in HTML.
2107 * Converts linebreaks into blanks; escapes single quotes
2108 * Target target buffer
2109 * Source source buffer; set to NULL if you just have a C-String
2110 * PlainIn Plain-C string to append; set to NULL if unused
2112 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2114 const char *aptr, *eiptr;
2118 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2121 if (PlainIn != NULL) {
2123 len = strlen(PlainIn);
2128 eiptr = aptr + Source->BufUsed;
2129 len = Source->BufUsed;
2135 eptr = Target->buf + Target->BufSize - 8;
2136 tptr = Target->buf + Target->BufUsed;
2138 while (aptr < eiptr){
2140 IncreaseBuf(Target, 1, -1);
2141 eptr = Target->buf + Target->BufSize - 8;
2142 tptr = Target->buf + Target->BufUsed;
2145 if (*aptr == '\n') {
2149 else if (*aptr == '\r') {
2153 else if (*aptr == '\'') {
2159 Target->BufUsed += 5;
2172 * Append a string, escaping characters which have meaning in ICAL.
2174 * Target target buffer
2175 * Source source buffer; set to NULL if you just have a C-String
2176 * PlainIn Plain-C string to append; set to NULL if unused
2178 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2180 const char *aptr, *eiptr;
2184 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2187 if (PlainIn != NULL) {
2189 len = strlen(PlainIn);
2194 eiptr = aptr + Source->BufUsed;
2195 len = Source->BufUsed;
2201 eptr = Target->buf + Target->BufSize - 8;
2202 tptr = Target->buf + Target->BufUsed;
2204 while (aptr < eiptr){
2205 if(tptr + 3 >= eptr) {
2206 IncreaseBuf(Target, 1, -1);
2207 eptr = Target->buf + Target->BufSize - 8;
2208 tptr = Target->buf + Target->BufUsed;
2211 if (*aptr == '\n') {
2218 else if (*aptr == '\r') {
2225 else if (*aptr == ',') {
2241 * Append a string, escaping characters which have meaning in JavaScript strings .
2243 * Target target buffer
2244 * Source source buffer; set to NULL if you just have a C-String
2245 * PlainIn Plain-C string to append; set to NULL if unused
2246 * @returns size of result or -1
2248 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2250 const char *aptr, *eiptr;
2255 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2258 if (PlainIn != NULL) {
2260 len = strlen(PlainIn);
2265 eiptr = aptr + Source->BufUsed;
2266 len = Source->BufUsed;
2272 bptr = Target->buf + Target->BufUsed;
2273 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2275 while (aptr < eiptr){
2277 IncreaseBuf(Target, 1, -1);
2278 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2279 bptr = Target->buf + Target->BufUsed;
2283 memcpy(bptr, HKEY("\\n"));
2285 Target->BufUsed += 2;
2288 memcpy(bptr, HKEY("\\r"));
2290 Target->BufUsed += 2;
2297 Target->BufUsed += 2;
2300 if ((*(aptr + 1) == 'u') &&
2301 isxdigit(*(aptr + 2)) &&
2302 isxdigit(*(aptr + 3)) &&
2303 isxdigit(*(aptr + 4)) &&
2304 isxdigit(*(aptr + 5)))
2305 { /* oh, a unicode escaper. let it pass through. */
2306 memcpy(bptr, aptr, 6);
2309 Target->BufUsed += 6;
2317 Target->BufUsed += 2;
2325 Target->BufUsed += 2;
2332 Target->BufUsed += 2;
2339 Target->BufUsed += 2;
2342 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2343 while (IsUtf8Sequence > 0){
2346 if (--IsUtf8Sequence)
2354 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2356 return Target->BufUsed;
2360 * Append a string, escaping characters which have meaning in HTML + json.
2362 * Target target buffer
2363 * Source source buffer; set to NULL if you just have a C-String
2364 * PlainIn Plain-C string to append; set to NULL if unused
2365 * nbsp If nonzero, spaces are converted to non-breaking spaces.
2366 * nolinebreaks if set to 1, linebreaks are removed from the string.
2367 * if set to 2, linebreaks are replaced by <br/>
2369 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2371 const char *aptr, *eiptr;
2374 int IsUtf8Sequence = 0;
2376 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2379 if (PlainIn != NULL) {
2381 len = strlen(PlainIn);
2386 eiptr = aptr + Source->BufUsed;
2387 len = Source->BufUsed;
2393 bptr = Target->buf + Target->BufUsed;
2394 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2396 while (aptr < eiptr){
2398 IncreaseBuf(Target, 1, -1);
2399 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2400 bptr = Target->buf + Target->BufUsed;
2404 memcpy(bptr, HKEY("<"));
2406 Target->BufUsed += 4;
2409 memcpy(bptr, HKEY(">"));
2411 Target->BufUsed += 4;
2414 memcpy(bptr, HKEY("&"));
2416 Target->BufUsed += 5;
2429 switch (nolinebreaks) {
2431 *bptr='\0'; /* nothing */
2434 memcpy(bptr, HKEY("<br/>"));
2436 Target->BufUsed += 11;
2439 memcpy(bptr, HKEY("\\n"));
2441 Target->BufUsed += 2;
2445 switch (nolinebreaks) {
2448 *bptr='\0'; /* nothing */
2451 memcpy(bptr, HKEY("\\r"));
2453 Target->BufUsed += 2;
2463 Target->BufUsed += 2;
2466 if ((*(aptr + 1) == 'u') &&
2467 isxdigit(*(aptr + 2)) &&
2468 isxdigit(*(aptr + 3)) &&
2469 isxdigit(*(aptr + 4)) &&
2470 isxdigit(*(aptr + 5)))
2471 { /* oh, a unicode escaper. let it pass through. */
2472 memcpy(bptr, aptr, 6);
2475 Target->BufUsed += 6;
2483 Target->BufUsed += 2;
2491 Target->BufUsed += 2;
2498 Target->BufUsed += 2;
2505 Target->BufUsed += 2;
2509 memcpy(bptr, HKEY(" "));
2511 Target->BufUsed += 6;
2515 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2516 while (IsUtf8Sequence > 0){
2519 if (--IsUtf8Sequence)
2527 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2529 return Target->BufUsed;
2534 * replace all non-Ascii characters by another
2535 * Buf buffer to inspect
2536 * repl charater to stamp over non ascii chars
2538 void StrBufAsciify(StrBuf *Buf, const char repl) {
2541 for (offset = 0; offset < Buf->BufUsed; offset ++)
2542 if (!isascii(Buf->buf[offset]))
2543 Buf->buf[offset] = repl;
2548 * unhide special chars hidden to the HTML escaper
2549 * target buffer to put the unescaped string in
2550 * source buffer to unescape
2552 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) {
2556 if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2561 FlushStrBuf(target);
2563 len = source->BufUsed;
2564 for (a = 0; a < len; ++a) {
2565 if (target->BufUsed >= target->BufSize)
2566 IncreaseBuf(target, 1, -1);
2568 if (source->buf[a] == '=') {
2569 hex[0] = source->buf[a + 1];
2570 hex[1] = source->buf[a + 2];
2573 sscanf(hex, "%02x", &b);
2574 target->buf[target->BufUsed] = b;
2575 target->buf[++target->BufUsed] = 0;
2579 target->buf[target->BufUsed] = source->buf[a];
2580 target->buf[++target->BufUsed] = 0;
2587 * hide special chars from the HTML escapers and friends
2588 * target buffer to put the escaped string in
2589 * source buffer to escape
2591 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) {
2595 FlushStrBuf(target);
2597 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2602 len = source->BufUsed;
2603 for (i=0; i<len; ++i) {
2604 if (target->BufUsed + 4 >= target->BufSize)
2605 IncreaseBuf(target, 1, -1);
2606 if ( (isalnum(source->buf[i])) ||
2607 (source->buf[i]=='-') ||
2608 (source->buf[i]=='_') ) {
2609 target->buf[target->BufUsed++] = source->buf[i];
2612 sprintf(&target->buf[target->BufUsed],
2614 (0xFF &source->buf[i]));
2615 target->BufUsed += 3;
2618 target->buf[target->BufUsed + 1] = '\0';
2622 /*******************************************************************************
2623 * Quoted Printable de/encoding *
2624 *******************************************************************************/
2627 * decode a buffer from base 64 encoding; destroys original
2628 * Buf Buffor to transform
2630 int StrBufDecodeBase64(StrBuf *Buf) {
2638 xferbuf = (char*) malloc(Buf->BufSize);
2639 if (xferbuf == NULL)
2643 siz = CtdlDecodeBase64(xferbuf, Buf->buf, Buf->BufUsed);
2648 Buf->buf[Buf->BufUsed] = '\0';
2654 * decode a buffer from base 64 encoding; expects targetbuffer
2655 * BufIn Buffor to transform
2656 * BufOut Buffer to put result into
2658 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut) {
2659 if ((BufIn == NULL) || (BufOut == NULL))
2662 if (BufOut->BufSize < BufIn->BufUsed) {
2663 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2666 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf, BufIn->buf, BufIn->BufUsed);
2667 return BufOut->BufUsed;
2670 typedef struct __z_enc_stream {
2676 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err) {
2677 //base64_decodestate *state;;
2682 //case eBase64Decode:
2683 //case eBase64Encode:
2684 //state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2685 //base64_init_decodestate(state);
2686 //return (vStreamT*) state;
2691 z_enc_stream *stream;
2694 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2695 memset(stream, 0, sizeof(z_enc_stream));
2696 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2697 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2699 err = inflateInit(&stream->zstream);
2702 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2706 return (vStreamT*) stream;
2711 z_enc_stream *stream;
2714 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2715 memset(stream, 0, sizeof(z_enc_stream));
2716 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2717 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2718 /* write gzip header */
2719 stream->OutBuf.BufUsed = snprintf
2720 (stream->OutBuf.buf,
2721 stream->OutBuf.BufSize,
2722 "%c%c%c%c%c%c%c%c%c%c",
2723 gz_magic[0], gz_magic[1], Z_DEFLATED,
2724 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2727 err = deflateInit2(&stream->zstream,
2728 ZLibCompressionRatio,
2732 Z_DEFAULT_STRATEGY);
2734 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2738 return (vStreamT*) stream;
2749 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err) {
2754 if ((vStream == NULL) || (*vStream==NULL)) {
2755 *Err = strerror(EINVAL);
2760 //case eBase64Encode:
2761 //case eBase64Decode:
2767 z_enc_stream *stream = (z_enc_stream *)*vStream;
2768 (void)inflateEnd(&stream->zstream);
2769 free(stream->OutBuf.buf);
2774 z_enc_stream *stream = (z_enc_stream *)*vStream;
2775 err = deflateEnd(&stream->zstream);
2780 free(stream->OutBuf.buf);
2791 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err) {
2795 //case eBase64Encode:
2800 //case eBase64Decode:
2807 z_enc_stream *stream = (z_enc_stream *)vStream;
2808 int org_outbuf_len = stream->OutBuf.BufUsed;
2810 unsigned int chunkavail;
2812 if (In->ReadWritePointer != NULL)
2814 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2815 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
2816 (In->ReadWritePointer - In->Buf->buf);
2820 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2821 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2824 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2825 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2827 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
2829 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
2831 if (Target && (LastChunk || (stream->OutBuf.BufUsed != org_outbuf_len))) {
2832 iSwapBuffers(Target->Buf, &stream->OutBuf);
2835 if (stream->zstream.avail_in == 0) {
2836 FlushStrBuf(In->Buf);
2837 In->ReadWritePointer = NULL;
2840 if (stream->zstream.avail_in < 64) {
2841 memmove(In->Buf->buf,
2842 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2843 stream->zstream.avail_in);
2845 In->Buf->BufUsed = stream->zstream.avail_in;
2846 In->Buf->buf[In->Buf->BufUsed] = '\0';
2849 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2852 rc = (LastChunk && (err != Z_FINISH));
2853 if (!rc && (err != Z_OK)) {
2860 z_enc_stream *stream = (z_enc_stream *)vStream;
2861 int org_outbuf_len = stream->zstream.total_out;
2864 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
2865 if (In->ReadWritePointer != NULL) {
2866 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2867 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - (In->ReadWritePointer - In->Buf->buf);
2870 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2871 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2875 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2876 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2878 err = inflate(&stream->zstream, Z_NO_FLUSH);
2880 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
2883 err = Z_DATA_ERROR; /* and fall through */
2888 (void)inflateEnd(&stream->zstream);
2892 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
2894 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
2896 if (stream->zstream.avail_in == 0) {
2897 FlushStrBuf(In->Buf);
2898 In->ReadWritePointer = NULL;
2901 if (stream->zstream.avail_in < 64) {
2902 memmove(In->Buf->buf,
2903 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2904 stream->zstream.avail_in);
2906 In->Buf->BufUsed = stream->zstream.avail_in;
2907 In->Buf->buf[In->Buf->BufUsed] = '\0';
2911 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2925 * decode a buffer from base 64 encoding; destroys original
2926 * Buf Buffor to transform
2928 int StrBufDecodeHex(StrBuf *Buf) {
2930 char *pch, *pche, *pchi;
2932 if (Buf == NULL) return -1;
2934 pch = pchi = Buf->buf;
2935 pche = pch + Buf->BufUsed;
2937 while (pchi < pche){
2938 ch = decode_hex(pchi);
2945 Buf->BufUsed = pch - Buf->buf;
2946 return Buf->BufUsed;
2950 * replace all chars >0x20 && < 0x7F with Mute
2951 * Mute char to put over invalid chars
2952 * Buf Buffor to transform
2954 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2958 if (Buf == NULL) return -1;
2959 pch = (unsigned char *)Buf->buf;
2960 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2961 if ((*pch < 0x20) || (*pch > 0x7F))
2965 return Buf->BufUsed;
2970 * remove escaped strings from i.e. the url string (like %20 for blanks)
2971 * Buf Buffer to translate
2972 * StripBlanks Reduce several blanks to one?
2974 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2983 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2984 Buf->buf[Buf->BufUsed - 1] = '\0';
2989 while (a < Buf->BufUsed) {
2990 if (Buf->buf[a] == '+')
2992 else if (Buf->buf[a] == '%') {
2993 /* don't let % chars through, rather truncate the input. */
2994 if (a + 2 > Buf->BufUsed) {
2999 hex[0] = Buf->buf[a + 1];
3000 hex[1] = Buf->buf[a + 2];
3003 sscanf(hex, "%02x", &b);
3004 Buf->buf[a] = (char) b;
3005 len = Buf->BufUsed - a - 2;
3007 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3019 * RFC2047-encode a header field if necessary.
3020 * If no non-ASCII characters are found, the string
3021 * will be copied verbatim without encoding.
3023 * target Target buffer.
3024 * source Source string to be encoded.
3025 * @returns encoded length; -1 if non success.
3027 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3029 const char headerStr[] = "=?UTF-8?Q?";
3030 int need_to_encode = 0;
3034 if ((source == NULL) ||
3038 while ((i < source->BufUsed) &&
3039 (!IsEmptyStr (&source->buf[i])) &&
3040 (need_to_encode == 0)) {
3041 if (((unsigned char) source->buf[i] < 32) ||
3042 ((unsigned char) source->buf[i] > 126)) {
3048 if (!need_to_encode) {
3049 if (*target == NULL) {
3050 *target = NewStrBufPlain(source->buf, source->BufUsed);
3053 FlushStrBuf(*target);
3054 StrBufAppendBuf(*target, source, 0);
3057 return (*target)->BufUsed;
3061 if (*target == NULL)
3062 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3063 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3064 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3065 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3066 (*target)->BufUsed = sizeof(headerStr) - 1;
3067 for (i=0; (i < source->BufUsed); ++i) {
3068 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3069 IncreaseBuf(*target, 1, 0);
3070 ch = (unsigned char) source->buf[i];
3079 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3080 (*target)->BufUsed += 3;
3084 (*target)->buf[(*target)->BufUsed] = '_';
3086 (*target)->buf[(*target)->BufUsed] = ch;
3087 (*target)->BufUsed++;
3091 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3092 IncreaseBuf(*target, 1, 0);
3094 (*target)->buf[(*target)->BufUsed++] = '?';
3095 (*target)->buf[(*target)->BufUsed++] = '=';
3096 (*target)->buf[(*target)->BufUsed] = '\0';
3097 return (*target)->BufUsed;;
3101 * Quoted-Printable encode a message; make it < 80 columns width.
3102 * source Source string to be encoded.
3103 * @returns buffer with encoded message.
3105 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3109 const char *ptr, *eptr;
3113 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3115 OEptr = OutBuf->buf + OutBuf->BufSize;
3116 ptr = EncodeMe->buf;
3117 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3122 if (Optr + 4 >= OEptr)
3125 Offset = Optr - OutBuf->buf;
3126 OutBuf->BufUsed = Optr - OutBuf->buf;
3127 IncreaseBuf(OutBuf, 1, 0);
3128 Optr = OutBuf->buf + Offset;
3129 OEptr = OutBuf->buf + OutBuf->BufSize;
3133 /* ignore carriage returns */
3136 else if (*ptr == '\n') {
3137 /* hard line break */
3138 memcpy(Optr, HKEY("=0A"));
3143 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3144 ( (*ptr >= 62) && (*ptr <= 126) ))
3155 *Optr = HexList[ch][0];
3157 *Optr = HexList[ch][1];
3164 /* soft line break */
3165 if (isspace(*(Optr - 1))) {
3170 *Optr = HexList[ch][0];
3172 *Optr = HexList[ch][1];
3184 OutBuf->BufUsed = Optr - OutBuf->buf;
3190 static void AddRecipient(StrBuf *Target,
3192 StrBuf *EmailAddress,
3197 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3198 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3200 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3201 StrBufRFC2047encode(&EncBuf, UserName);
3202 StrBufAppendBuf(Target, EncBuf, 0);
3203 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3204 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3206 if (StrLength(EmailAddress) > 0){
3207 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3208 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3209 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3215 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3216 * \param Recp Source list of email recipients
3217 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3218 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3219 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3220 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3222 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
3224 StrBuf *EmailAddress,
3228 const char *pch, *pche;
3229 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3231 if ((Recp == NULL) || (StrLength(Recp) == 0))
3235 pche = pch + StrLength(Recp);
3237 if (!CheckEncode(pch, -1, pche))
3238 return NewStrBufDup(Recp);
3240 Target = NewStrBufPlain(NULL, StrLength(Recp));
3242 while ((pch != NULL) && (pch < pche))
3244 while (isspace(*pch)) pch++;
3245 UserEnd = EmailStart = EmailEnd = NULL;
3247 if ((*pch == '"') || (*pch == '\'')) {
3248 UserStart = pch + 1;
3250 UserEnd = strchr(UserStart, *pch);
3251 if (UserEnd == NULL)
3252 break; ///TODO: Userfeedback??
3253 EmailStart = UserEnd + 1;
3254 while (isspace(*EmailStart))
3256 if (UserEnd == UserStart) {
3257 UserStart = UserEnd = NULL;
3260 if (*EmailStart == '<') {
3262 EmailEnd = strchr(EmailStart, '>');
3263 if (EmailEnd == NULL)
3264 EmailEnd = strchr(EmailStart, ',');
3268 EmailEnd = strchr(EmailStart, ',');
3270 if (EmailEnd == NULL)
3277 EmailEnd = strchr(UserStart, ',');
3278 if (EmailEnd == NULL) {
3279 EmailEnd = strchr(pch, '>');
3281 if (EmailEnd != NULL) {
3291 while ((EmailEnd > UserStart) && !gt &&
3292 ((*EmailEnd == ',') ||
3293 (*EmailEnd == '>') ||
3294 (isspace(*EmailEnd))))
3296 if (*EmailEnd == '>')
3301 if (EmailEnd == UserStart)
3305 EmailStart = strchr(UserStart, '<');
3306 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3308 UserEnd = EmailStart;
3310 while ((UserEnd > UserStart) &&
3311 isspace (*(UserEnd - 1)))
3314 if (UserStart >= UserEnd)
3315 UserStart = UserEnd = NULL;
3317 else { /* this is a local recipient... no domain, just a realname */
3318 EmailStart = UserStart;
3319 At = strchr(EmailStart, '@');
3325 EmailStart = UserStart;
3331 if ((UserStart != NULL) && (UserEnd != NULL))
3332 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3333 else if ((UserStart != NULL) && (UserEnd == NULL))
3334 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3336 FlushStrBuf(UserName);
3338 if ((EmailStart != NULL) && (EmailEnd != NULL))
3339 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3340 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3341 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3343 FlushStrBuf(EmailAddress);
3345 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3350 if ((pch != NULL) && (*pch == ','))
3352 if (pch != NULL) while (isspace(*pch))
3360 * replaces all occurances of 'search' by 'replace'
3361 * buf Buffer to modify
3362 * search character to search
3363 * replace character to replace search by
3365 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3370 for (i=0; i<buf->BufUsed; i++)
3371 if (buf->buf[i] == search)
3372 buf->buf[i] = replace;
3377 * removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3378 * buf Buffer to modify
3380 void StrBufToUnixLF(StrBuf *buf)
3382 char *pche, *pchS, *pchT;
3386 pche = buf->buf + buf->BufUsed;
3387 pchS = pchT = buf->buf;
3393 if (*pchS != '\n') {
3402 buf->BufUsed = pchT - buf->buf;
3406 /*******************************************************************************
3407 * Iconv Wrapper; RFC822 de/encoding *
3408 *******************************************************************************/
3411 * Wrapper around iconv_open()
3412 * Our version adds aliases for non-standard Microsoft charsets
3413 * such as 'MS950', aliasing them to names like 'CP950'
3415 * tocode Target encoding
3416 * fromcode Source encoding
3417 * pic anonimized pointer to iconv struct
3419 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3422 iconv_t ic = (iconv_t)(-1) ;
3423 ic = iconv_open(tocode, fromcode);
3424 if (ic == (iconv_t)(-1) ) {
3425 char alias_fromcode[64];
3426 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3427 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3428 alias_fromcode[0] = 'C';
3429 alias_fromcode[1] = 'P';
3430 ic = iconv_open(tocode, alias_fromcode);
3433 *(iconv_t *)pic = ic;
3439 * find one chunk of a RFC822 encoded string
3440 * Buffer where to search
3441 * bptr where to start searching
3442 * @returns found position, NULL if none.
3444 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3447 /* Find the next ?Q? */
3448 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3451 end = strchr(bptr + 2, '?');
3456 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3457 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3458 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3459 (*(end + 2) == '?')) {
3460 /* skip on to the end of the cluster, the next ?= */
3461 end = strstr(end + 3, "?=");
3464 /* sort of half valid encoding, try to find an end. */
3465 end = strstr(bptr, "?=");
3472 * convert one buffer according to the preselected iconv pointer PIC
3473 * ConvertBuf buffer we need to translate
3474 * TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3475 * pic Pointer to the iconv-session Object
3477 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3483 char *ibuf; /**< Buffer of characters to be converted */
3484 char *obuf; /**< Buffer for converted characters */
3485 size_t ibuflen; /**< Length of input buffer */
3486 size_t obuflen; /**< Length of output buffer */
3489 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3492 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3493 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3494 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3496 ic = *(iconv_t*)pic;
3497 ibuf = ConvertBuf->buf;
3498 ibuflen = ConvertBuf->BufUsed;
3500 obuflen = TmpBuf->BufSize;
3502 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3505 if (errno == E2BIG) {
3507 IncreaseBuf(TmpBuf, 0, 0);
3512 else if (errno == EILSEQ){
3513 /* hm, invalid utf8 sequence... what to do now? */
3514 /* An invalid multibyte sequence has been encountered in the input */
3516 else if (errno == EINVAL) {
3517 /* An incomplete multibyte sequence has been encountered in the input. */
3520 FlushStrBuf(TmpBuf);
3523 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3524 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3526 /* little card game: wheres the red lady? */
3527 iSwapBuffers(ConvertBuf, TmpBuf);
3528 FlushStrBuf(TmpBuf);
3535 * catches one RFC822 encoded segment, and decodes it.
3536 * Target buffer to fill with result
3537 * DecodeMe buffer with stuff to process
3538 * SegmentStart points to our current segment in DecodeMe
3539 * SegmentEnd Points to the end of our current segment in DecodeMe
3540 * ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3541 * ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3542 * FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3544 inline static void DecodeSegment(StrBuf *Target,
3545 const StrBuf *DecodeMe,
3546 const char *SegmentStart,
3547 const char *SegmentEnd,
3549 StrBuf *ConvertBuf2,
3550 StrBuf *FoundCharset)
3556 iconv_t ic = (iconv_t)(-1);
3560 /* Now we handle foreign character sets properly encoded
3561 * in RFC2047 format.
3563 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3564 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3565 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3566 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3567 if (FoundCharset != NULL) {
3568 FlushStrBuf(FoundCharset);
3569 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3571 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3572 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3574 *encoding = toupper(*encoding);
3575 if (*encoding == 'B') { /**< base64 */
3576 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3577 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3578 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3580 ConvertBuf->BufUsed);
3582 else if (*encoding == 'Q') { /**< quoted-printable */
3586 while (pos < ConvertBuf->BufUsed)
3588 if (ConvertBuf->buf[pos] == '_')
3589 ConvertBuf->buf[pos] = ' ';
3593 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3594 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3596 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3599 ConvertBuf->BufUsed);
3602 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3605 ctdl_iconv_open("UTF-8", charset, &ic);
3606 if (ic != (iconv_t)(-1) ) {
3608 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3609 StrBufAppendBuf(Target, ConvertBuf2, 0);
3614 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3620 * Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3621 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3622 * Target where to put the decoded string to
3623 * DecodeMe buffer with encoded string
3624 * DefaultCharset if we don't find one, which should we use?
3625 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3626 * put it here for later use where no string might be known.
3628 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3631 StrBuf *ConvertBuf2;
3632 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3633 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3635 StrBuf_RFC822_2_Utf8(Target,
3641 FreeStrBuf(&ConvertBuf);
3642 FreeStrBuf(&ConvertBuf2);
3646 * Handle subjects with RFC2047 encoding such as:
3647 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3648 * Target where to put the decoded string to
3649 * DecodeMe buffer with encoded string
3650 * DefaultCharset if we don't find one, which should we use?
3651 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3652 * put it here for later use where no string might be known.
3653 * ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3654 * ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3656 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3657 const StrBuf *DecodeMe,
3658 const StrBuf* DefaultCharset,
3659 StrBuf *FoundCharset,
3661 StrBuf *ConvertBuf2)
3663 StrBuf *DecodedInvalidBuf = NULL;
3664 const StrBuf *DecodeMee = DecodeMe;
3665 const char *start, *end, *next, *nextend, *ptr = NULL;
3667 iconv_t ic = (iconv_t)(-1) ;
3672 int illegal_non_rfc2047_encoding = 0;
3675 if (DecodeMe == NULL)
3677 /* Sometimes, badly formed messages contain strings which were simply
3678 * written out directly in some foreign character set instead of
3679 * using RFC2047 encoding. This is illegal but we will attempt to
3680 * handle it anyway by converting from a user-specified default
3681 * charset to UTF-8 if we see any nonprintable characters.
3684 for (i=0; i<DecodeMe->BufUsed; ++i) {
3685 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3686 illegal_non_rfc2047_encoding = 1;
3691 if ((illegal_non_rfc2047_encoding) &&
3692 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3693 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3696 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3697 if (ic != (iconv_t)(-1) ) {
3698 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3699 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3700 DecodeMee = DecodedInvalidBuf;
3706 /* pre evaluate the first pair */
3708 start = strstr(DecodeMee->buf, "=?");
3709 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3711 end = FindNextEnd (DecodeMee, start + 2);
3713 StrBufAppendBuf(Target, DecodeMee, 0);
3714 FreeStrBuf(&DecodedInvalidBuf);
3719 if (start != DecodeMee->buf) {
3722 nFront = start - DecodeMee->buf;
3723 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3726 * Since spammers will go to all sorts of absurd lengths to get their
3727 * messages through, there are LOTS of corrupt headers out there.
3728 * So, prevent a really badly formed RFC2047 header from throwing
3729 * this function into an infinite loop.
3731 while ((start != NULL) &&
3738 DecodeSegment(Target,
3746 next = strstr(end, "=?");
3748 if ((next != NULL) &&
3750 nextend = FindNextEnd(DecodeMee, next);
3751 if (nextend == NULL)
3754 /* did we find two partitions */
3755 if ((next != NULL) &&
3759 while ((ptr < next) &&
3766 * did we find a gab just filled with blanks?
3767 * if not, copy its stuff over.
3771 StrBufAppendBufPlain(Target,
3777 /* our next-pair is our new first pair now. */
3783 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3784 if ((end != NULL) && (end < nextend)) {
3786 while ( (ptr < nextend) &&
3793 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3795 FreeStrBuf(&DecodedInvalidBuf);
3798 /*******************************************************************************
3799 * Manipulating UTF-8 Strings *
3800 *******************************************************************************/
3803 * evaluate the length of an utf8 special character sequence
3804 * Char the character to examine
3805 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3807 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3810 unsigned char test = (1<<7);
3812 if ((*CharS & 0xC0) != 0xC0)
3816 ((test & ((unsigned char)*CharS)) != 0))
3821 if ((n > 6) || ((CharE - CharS) < n))
3827 * detect whether this char starts an utf-8 encoded char
3828 * Char character to inspect
3829 * @returns yes or no
3831 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3833 /** 11??.???? indicates an UTF8 Sequence. */
3834 return ((Char & 0xC0) == 0xC0);
3838 * measure the number of glyphs in an UTF8 string...
3839 * Buf string to measure
3840 * @returns the number of glyphs in Buf
3842 long StrBuf_Utf8StrLen(StrBuf *Buf)
3848 if ((Buf == NULL) || (Buf->BufUsed == 0))
3851 eptr = Buf->buf + Buf->BufUsed;
3852 while ((aptr < eptr) && (*aptr != '\0')) {
3853 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3854 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3855 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3867 * cuts a string after maxlen glyphs
3868 * Buf string to cut to maxlen glyphs
3869 * maxlen how long may the string become?
3870 * @returns current length of the string
3872 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3878 eptr = Buf->buf + Buf->BufUsed;
3879 while ((aptr < eptr) && (*aptr != '\0')) {
3880 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3881 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3882 while ((*aptr++ != '\0') && (m-- > 0));
3891 Buf->BufUsed = aptr - Buf->buf;
3892 return Buf->BufUsed;
3895 return Buf->BufUsed;
3903 /*******************************************************************************
3905 *******************************************************************************/
3908 * uses the same calling syntax as compress2(), but it
3909 * creates a stream compatible with HTTP "Content-encoding: gzip"
3910 * dest compressed buffer
3911 * destLen length of the compresed data
3912 * source source to encode
3913 * sourceLen length of source to encode
3914 * level compression level
3917 int ZEXPORT compress_gzip(Bytef * dest,
3919 const Bytef * source,
3923 /* write gzip header */
3924 snprintf((char *) dest, *destLen,
3925 "%c%c%c%c%c%c%c%c%c%c",
3926 gz_magic[0], gz_magic[1], Z_DEFLATED,
3927 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3930 /* normal deflate */
3933 stream.next_in = (Bytef *) source;
3934 stream.avail_in = (uInt) sourceLen;
3935 stream.next_out = dest + 10L; // after header
3936 stream.avail_out = (uInt) * destLen;
3937 if ((uLong) stream.avail_out != *destLen)
3940 stream.zalloc = (alloc_func) 0;
3941 stream.zfree = (free_func) 0;
3942 stream.opaque = (voidpf) 0;
3944 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3945 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3949 err = deflate(&stream, Z_FINISH);
3950 if (err != Z_STREAM_END) {
3951 deflateEnd(&stream);
3952 return err == Z_OK ? Z_BUF_ERROR : err;
3954 *destLen = stream.total_out + 10L;
3956 /* write CRC and Length */
3957 uLong crc = crc32(0L, source, sourceLen);
3959 for (n = 0; n < 4; ++n, ++*destLen) {
3960 dest[*destLen] = (int) (crc & 0xff);
3963 uLong len = stream.total_in;
3964 for (n = 0; n < 4; ++n, ++*destLen) {
3965 dest[*destLen] = (int) (len & 0xff);
3968 err = deflateEnd(&stream);
3975 * compress the buffer with gzip
3976 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3977 * Buf buffer whose content is to be gzipped
3979 int CompressBuffer(StrBuf *Buf)
3982 char *compressed_data = NULL;
3983 size_t compressed_len, bufsize;
3986 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3987 compressed_data = malloc(compressed_len);
3989 if (compressed_data == NULL)
3991 /* Flush some space after the used payload so valgrind shuts up... */
3992 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3993 Buf->buf[Buf->BufUsed + i++] = '\0';
3994 if (compress_gzip((Bytef *) compressed_data,
3997 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4000 Buf->buf = compressed_data;
4001 Buf->BufUsed = compressed_len;
4002 Buf->BufSize = bufsize;
4003 /* Flush some space after the used payload so valgrind shuts up... */
4005 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4006 Buf->buf[Buf->BufUsed + i++] = '\0';
4009 free(compressed_data);
4011 #endif /* HAVE_ZLIB */
4015 /*******************************************************************************
4016 * File I/O; Callbacks to libevent *
4017 *******************************************************************************/
4019 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4024 if ((FB == NULL) || (FB->Buf == NULL))
4028 * check whether the read pointer is somewhere in a range
4029 * where a cut left is inexpensive
4032 if (FB->ReadWritePointer != NULL)
4036 already_read = FB->ReadWritePointer - FB->Buf->buf;
4037 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4039 if (already_read != 0) {
4042 unread = FB->Buf->BufUsed - already_read;
4044 /* else nothing to compact... */
4046 FB->ReadWritePointer = FB->Buf->buf;
4047 bufremain = FB->Buf->BufSize;
4049 else if ((unread < 64) ||
4050 (bufremain < already_read))
4053 * if its just a tiny bit remaining, or we run out of space...
4056 FB->Buf->BufUsed = unread;
4057 if (unread < already_read)
4058 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4060 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4061 FB->ReadWritePointer = FB->Buf->buf;
4062 bufremain = FB->Buf->BufSize - unread - 1;
4064 else if (bufremain < (FB->Buf->BufSize / 10))
4066 /* get a bigger buffer */
4068 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4070 FB->ReadWritePointer = FB->Buf->buf + unread;
4072 bufremain = FB->Buf->BufSize - unread - 1;
4073 /*TODO: special increase function that won't copy the already read! */
4076 else if (bufremain < 10) {
4077 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4079 FB->ReadWritePointer = FB->Buf->buf;
4081 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4086 FB->ReadWritePointer = FB->Buf->buf;
4087 bufremain = FB->Buf->BufSize - 1;
4090 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4093 FB->Buf->BufUsed += n;
4094 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4099 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4104 if ((FB == NULL) || (FB->Buf == NULL))
4107 if (FB->ReadWritePointer != NULL)
4109 WriteRemain = FB->Buf->BufUsed -
4110 (FB->ReadWritePointer -
4114 FB->ReadWritePointer = FB->Buf->buf;
4115 WriteRemain = FB->Buf->BufUsed;
4118 n = write(fd, FB->ReadWritePointer, WriteRemain);
4120 FB->ReadWritePointer += n;
4122 if (FB->ReadWritePointer ==
4123 FB->Buf->buf + FB->Buf->BufUsed)
4125 FlushStrBuf(FB->Buf);
4126 FB->ReadWritePointer = NULL;
4129 // check whether we've got something to write
4130 // get the maximum chunk plus the pointer we can send
4131 // write whats there
4132 // if not all was sent, remember the send pointer for the next time
4133 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4139 * extract a "next line" from Buf; Ptr to persist across several iterations
4140 * LineBuf your line will be copied here.
4141 * FB BLOB with lines of text...
4142 * Ptr moved arround to keep the next-line across several iterations
4143 * has to be &NULL on start; will be &NotNULL on end of buffer
4144 * @returns size of copied buffer
4146 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4148 const char *aptr, *ptr, *eptr;
4151 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4155 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4156 FB->ReadWritePointer = StrBufNOTNULL;
4160 FlushStrBuf(LineBuf);
4161 if (FB->ReadWritePointer == NULL)
4162 ptr = aptr = FB->Buf->buf;
4164 ptr = aptr = FB->ReadWritePointer;
4166 optr = LineBuf->buf;
4167 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4168 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4170 while ((ptr <= eptr) &&
4177 LineBuf->BufUsed = optr - LineBuf->buf;
4178 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4179 optr = LineBuf->buf + LineBuf->BufUsed;
4180 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4185 if (optr > LineBuf->buf)
4187 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4188 LineBuf->BufUsed = optr - LineBuf->buf;
4190 if ((FB->ReadWritePointer != NULL) &&
4191 (FB->ReadWritePointer != FB->Buf->buf))
4193 /* Ok, the client application read all the data
4194 it was interested in so far. Since there is more to read,
4195 we now shrink the buffer, and move the rest over.
4197 StrBufCutLeft(FB->Buf,
4198 FB->ReadWritePointer - FB->Buf->buf);
4199 FB->ReadWritePointer = FB->Buf->buf;
4201 return eMustReadMore;
4204 LineBuf->BufUsed = optr - LineBuf->buf;
4206 if ((ptr <= eptr) && (*ptr == '\r'))
4208 if ((ptr <= eptr) && (*ptr == '\n'))
4212 FB->ReadWritePointer = ptr;
4215 FlushStrBuf(FB->Buf);
4216 FB->ReadWritePointer = NULL;
4219 return eReadSuccess;
4223 * check whether the chunk-buffer has more data waiting or not.
4224 * FB Chunk-Buffer to inspect
4226 eReadState StrBufCheckBuffer(IOBuffer *FB)
4230 if (FB->Buf->BufUsed == 0)
4231 return eReadSuccess;
4232 if (FB->ReadWritePointer == NULL)
4233 return eBufferNotEmpty;
4234 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4235 return eBufferNotEmpty;
4236 return eReadSuccess;
4239 long IOBufferStrLength(IOBuffer *FB)
4241 if ((FB == NULL) || (FB->Buf == NULL))
4243 if (FB->ReadWritePointer == NULL)
4244 return StrLength(FB->Buf);
4246 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4250 /*******************************************************************************
4251 * File I/O; Prefer buffered read since its faster! *
4252 *******************************************************************************/
4255 * Read a line from socket
4256 * flushes and closes the FD on error
4257 * buf the buffer to get the input to
4258 * fd pointer to the filedescriptor to read
4259 * append Append to an existing string or replace?
4260 * Error strerror() on error
4261 * @returns numbers of chars read
4263 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4265 int len, rlen, slen;
4267 if ((buf == NULL) || (buf->buf == NULL)) {
4268 *Error = strerror(EINVAL);
4275 slen = len = buf->BufUsed;
4277 rlen = read(*fd, &buf->buf[len], 1);
4279 *Error = strerror(errno);
4286 if (buf->buf[len] == '\n')
4288 if (buf->buf[len] != '\r')
4290 if (len + 2 >= buf->BufSize) {
4292 buf->buf[len+1] = '\0';
4293 IncreaseBuf(buf, 1, -1);
4297 buf->buf[len] = '\0';
4303 * Read a line from socket
4304 * flushes and closes the FD on error
4305 * Line the line to read from the fd / I/O Buffer
4306 * buf the buffer to get the input to
4307 * fd pointer to the filedescriptor to read
4308 * timeout number of successless selects until we bail out
4309 * selectresolution how long to wait on each select
4310 * Error strerror() on error
4311 * @returns numbers of chars read
4313 int StrBufTCP_read_buffered_line(StrBuf *Line,
4317 int selectresolution,
4321 int nSuccessLess = 0;
4328 if (buf->BufUsed > 0) {
4329 pch = strchr(buf->buf, '\n');
4332 len = pch - buf->buf;
4333 if (len > 0 && (*(pch - 1) == '\r') )
4335 StrBufSub(Line, buf, 0, len - rlen);
4336 StrBufCutLeft(buf, len + 1);
4341 if (buf->BufSize - buf->BufUsed < 10)
4342 IncreaseBuf(buf, 1, -1);
4344 fdflags = fcntl(*fd, F_GETFL);
4345 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4347 while ((nSuccessLess < timeout) && (pch == NULL)) {
4349 tv.tv_sec = selectresolution;
4354 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4355 *Error = strerror(errno);
4361 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4366 &buf->buf[buf->BufUsed],
4367 buf->BufSize - buf->BufUsed - 1);
4369 *Error = strerror(errno);
4374 else if (rlen > 0) {
4376 buf->BufUsed += rlen;
4377 buf->buf[buf->BufUsed] = '\0';
4378 pch = strchr(buf->buf, '\n');
4379 if ((pch == NULL) &&
4380 (buf->BufUsed + 10 > buf->BufSize) &&
4381 (IncreaseBuf(buf, 1, -1) == -1))
4389 len = pch - buf->buf;
4390 if (len > 0 && (*(pch - 1) == '\r') )
4392 StrBufSub(Line, buf, 0, len - rlen);
4393 StrBufCutLeft(buf, len + 1);
4400 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4401 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4402 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4404 * Read a line from socket
4405 * flushes and closes the FD on error
4406 * Line where to append our Line read from the fd / I/O Buffer;
4407 * IOBuf the buffer to get the input to; lifetime pair to FD
4408 * Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4409 * fd pointer to the filedescriptor to read
4410 * timeout number of successless selects until we bail out
4411 * selectresolution how long to wait on each select
4412 * Error strerror() on error
4413 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4415 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4420 int selectresolution,
4423 const char *pche = NULL;
4424 const char *pos = NULL;
4426 int len, rlen, retlen;
4427 int nSuccessLess = 0;
4429 const char *pch = NULL;
4435 if ((Line == NULL) ||
4442 *Error = ErrRBLF_PreConditionFailed;
4447 if ((IOBuf->BufUsed > 0) &&
4449 (pos < IOBuf->buf + IOBuf->BufUsed))
4453 pche = IOBuf->buf + IOBuf->BufUsed;
4457 while ((pch < pche) && (*pch != '\n'))
4459 if (Line->BufUsed + 10 > Line->BufSize)
4462 apos = pcht - Line->buf;
4464 IncreaseBuf(Line, 1, -1);
4465 pcht = Line->buf + apos;
4473 if (len > 0 && (*(pch - 1) == '\r') )
4482 if ((pch >= pche) || (*pch == '\0'))
4490 if ((pch != NULL) &&
4493 if (pch + 1 >= pche) {
4506 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4508 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4509 IncreaseBuf(IOBuf, 1, -1);
4511 fdflags = fcntl(*fd, F_GETFL);
4512 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4515 while ((nSuccessLess < timeout) &&
4525 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4526 *Error = strerror(errno);
4530 *Error = ErrRBLF_SelectFailed;
4533 if (! FD_ISSET(*fd, &rfds) != 0) {
4539 &IOBuf->buf[IOBuf->BufUsed],
4540 IOBuf->BufSize - IOBuf->BufUsed - 1);
4542 *Error = strerror(errno);
4547 else if (rlen > 0) {
4549 pLF = IOBuf->buf + IOBuf->BufUsed;
4550 IOBuf->BufUsed += rlen;
4551 IOBuf->buf[IOBuf->BufUsed] = '\0';
4553 pche = IOBuf->buf + IOBuf->BufUsed;
4555 while ((pLF < pche) && (*pLF != '\n'))
4557 if ((pLF >= pche) || (*pLF == '\0'))
4560 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4564 if (pLF != NULL) apos = pLF - IOBuf->buf;
4565 IncreaseBuf(IOBuf, 1, -1);
4566 if (pLF != NULL) pLF = IOBuf->buf + apos;
4580 if (len > 0 && (*(pLF - 1) == '\r') )
4582 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4583 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4589 return retlen + len;
4591 *Error = ErrRBLF_NotEnoughSentFromServer;
4596 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4598 * Input binary data from socket
4599 * flushes and closes the FD on error
4600 * Buf the buffer to get the input to
4601 * fd pointer to the filedescriptor to read
4602 * append Append to an existing string or replace?
4603 * nBytes the maximal number of bytes to read
4604 * Error strerror() on error
4605 * @returns numbers of chars read
4607 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4618 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4620 *Error = ErrRBLF_BLOBPreConditionFailed;
4625 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4626 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4628 ptr = Buf->buf + Buf->BufUsed;
4630 fdflags = fcntl(*fd, F_GETFL);
4631 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4633 while ((nRead < nBytes) &&
4643 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4644 *Error = strerror(errno);
4648 *Error = ErrRBLF_SelectFailed;
4651 if (! FD_ISSET(*fd, &rfds) != 0) {
4657 if ((rlen = read(*fd,
4659 nBytes - nRead)) == -1) {
4662 *Error = strerror(errno);
4667 Buf->BufUsed += rlen;
4669 Buf->buf[Buf->BufUsed] = '\0';
4673 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4674 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4676 * Input binary data from socket
4677 * flushes and closes the FD on error
4678 * Blob put binary thing here
4679 * IOBuf the buffer to get the input to
4680 * Pos offset inside of IOBuf
4681 * fd pointer to the filedescriptor to read
4682 * append Append to an existing string or replace?
4683 * nBytes the maximal number of bytes to read
4684 * check whether we should search for '000\n' terminators in case of timeouts
4685 * Error strerror() on error
4686 * @returns numbers of chars read
4688 int StrBufReadBLOBBuffered(StrBuf *Blob,
4701 int nAlreadyRead = 0;
4706 int nSuccessLess = 0;
4709 if ((Blob == NULL) ||
4716 *Error = ErrRBB_BLOBFPreConditionFailed;
4722 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4723 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4728 rlen = pos - IOBuf->buf;
4730 rlen = IOBuf->BufUsed - rlen;
4733 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed))
4735 if (rlen < nBytes) {
4736 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4737 Blob->BufUsed += rlen;
4738 Blob->buf[Blob->BufUsed] = '\0';
4739 nAlreadyRead = nRead = rlen;
4742 if (rlen >= nBytes) {
4743 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4744 Blob->BufUsed += nBytes;
4745 Blob->buf[Blob->BufUsed] = '\0';
4746 if (rlen == nBytes) {
4758 if (IOBuf->BufSize < nBytes - nRead) {
4759 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4763 fdflags = fcntl(*fd, F_GETFL);
4764 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4772 while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
4779 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4780 *Error = strerror(errno);
4783 if (*Error == NULL) {
4784 *Error = ErrRBLF_SelectFailed;
4788 if (! FD_ISSET(*fd, &rfds) != 0) {
4793 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
4794 // if (rlen == -1) { 2021feb27 ajc changed this, apparently we will always get at least 1 byte unless the connection is broken
4798 *Error = strerror(errno);
4801 else if (rlen == 0){
4802 if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
4803 StrBufPlain(Blob, HKEY("\n000\n"));
4804 StrBufCutRight(Blob, 5);
4805 return Blob->BufUsed;
4807 else if (!IsNonBlock)
4809 else if (nSuccessLess > MaxTries) {
4811 *Error = ErrRBB_too_many_selects;
4815 else if (rlen > 0) {
4819 IOBuf->BufUsed += rlen;
4822 if (nSuccessLess >= MaxTries) {
4824 *Error = ErrRBB_too_many_selects;
4828 if (nRead > nBytes) {
4829 *Pos = IOBuf->buf + nBytes;
4831 Blob->buf[Blob->BufUsed] = '\0';
4832 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4836 return nRead + nAlreadyRead;
4840 * extract a "next line" from Buf; Ptr to persist across several iterations
4841 * LineBuf your line will be copied here.
4842 * Buf BLOB with lines of text...
4843 * Ptr moved arround to keep the next-line across several iterations
4844 * has to be &NULL on start; will be &NotNULL on end of buffer
4845 * @returns size of remaining buffer
4847 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
4849 const char *aptr, *ptr, *eptr;
4852 if ((Buf == NULL) ||
4853 (*Ptr == StrBufNOTNULL) ||
4855 (LineBuf->buf == NULL))
4857 *Ptr = StrBufNOTNULL;
4861 FlushStrBuf(LineBuf);
4863 ptr = aptr = Buf->buf;
4867 optr = LineBuf->buf;
4868 eptr = Buf->buf + Buf->BufUsed;
4869 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4871 while ((ptr <= eptr) &&
4878 LineBuf->BufUsed = optr - LineBuf->buf;
4879 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4880 optr = LineBuf->buf + LineBuf->BufUsed;
4881 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4885 if ((ptr >= eptr) && (optr > LineBuf->buf))
4887 LineBuf->BufUsed = optr - LineBuf->buf;
4889 if ((ptr <= eptr) && (*ptr == '\r'))
4891 if ((ptr <= eptr) && (*ptr == '\n'))
4898 *Ptr = StrBufNOTNULL;
4901 return Buf->BufUsed - (ptr - Buf->buf);
4906 * removes double slashes from pathnames
4907 * Dir directory string to filter
4908 * RemoveTrailingSlash allows / disallows trailing slashes
4910 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
4916 while (!IsEmptyStr(a)) {
4928 if ((RemoveTrailingSlash) &&
4934 Dir->BufUsed = b - Dir->buf;
4939 * Decode a quoted-printable encoded StrBuf buffer "in place"
4940 * This is possible because the decoded will always be shorter than the encoded
4941 * so we don't have to worry about the buffer being to small.
4943 void StrBufDecodeQP(StrBuf *Buf)
4945 if (!Buf) { // sanity check #1
4949 int source_len = StrLength(Buf);
4950 if (source_len < 1) { // sanity check #2
4954 int spos = 0; // source position
4955 int tpos = 0; // target position
4957 while (spos < source_len) {
4958 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
4961 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
4964 else if (Buf->buf[spos] == '=') {
4967 sscanf(&Buf->buf[spos], "%02x", &ch);
4968 Buf->buf[tpos++] = ch;
4972 Buf->buf[tpos++] = Buf->buf[spos++];
4977 Buf->BufUsed = tpos;