1 // Copyright (c) 1987-2023 by the citadel.org team
3 // This program is open source software. Use, duplication, or disclosure
4 // is subject to the terms of the GNU General Public License, version 3.
14 #include <sys/select.h>
16 #include <sys/types.h>
17 #define SHOW_ME_VAPPEND_PRINTF
20 #include "libcitadel.h"
36 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen, const Bytef * source, uLong sourceLen, int level);
38 int BaseStrBufSize = 64;
40 int ZLibCompressionRatio = -1; /* defaults to 6 */
42 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
43 #define OS_CODE 0x03 /*< unix */
44 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
47 const char *StrBufNOTNULL = ((char*) NULL) - 1;
49 const char HexList[256][3] = {
50 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
51 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
52 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
53 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
54 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
55 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
56 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
57 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
58 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
59 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
60 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
61 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
62 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
63 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
64 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
65 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
68 // Private Structure for the Stringbuffer
70 char *buf; // the pointer to the dynamic buffer
71 long BufSize; // how many spcae do we optain
72 long BufUsed; // Number of Chars used excluding the trailing \\0
73 int ConstBuf; // are we just a wrapper arround a static buffer and musn't we be changed?
75 long nIncreases; // for profiling; cound how many times we needed more
76 char bt [SIZ]; // Stacktrace of last increase
77 char bt_lastinc[SIZ]; // How much did we increase last time?
82 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
83 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
87 static void StrBufBacktrace(StrBuf *Buf, int which) {
90 void *stack_frames[50];
95 pstart = pch = Buf->bt;
98 pstart = pch = Buf->bt_lastinc;
100 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
101 strings = backtrace_symbols(stack_frames, size);
102 for (i = 0; i < size; i++) {
103 if (strings != NULL) {
104 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
107 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
116 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere) {
117 if (hFreeDbglog == -1) {
118 pid_t pid = getpid();
120 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
121 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
123 if ((*FreeMe)->nIncreases > 0) {
126 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
128 (*FreeMe)->nIncreases,
132 (*FreeMe)->bt_lastinc);
133 n = write(hFreeDbglog, buf, n);
138 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
142 n = write(hFreeDbglog, buf, n);
147 void dbg_IncreaseBuf(StrBuf *IncMe) {
149 #ifdef HAVE_BACKTRACE
150 StrBufBacktrace(Buf, 1);
155 void dbg_Init(StrBuf *Buf) {
158 Buf->bt_lastinc[0] = '\0';
159 #ifdef HAVE_BACKTRACE
160 StrBufBacktrace(Buf, 0);
166 #define dbg_FreeStrBuf(a, b)
167 #define dbg_IncreaseBuf(a)
173 * swaps the contents of two StrBufs
174 * this is to be used to have cheap switched between a work-buffer and a target buffer
178 static inline void iSwapBuffers(StrBuf *A, StrBuf *B) {
181 memcpy(&C, A, sizeof(*A));
182 memcpy(A, B, sizeof(*B));
183 memcpy(B, &C, sizeof(C));
188 void SwapBuffers(StrBuf *A, StrBuf *B) {
194 * Cast operator to Plain String
195 * @note if the buffer is altered by StrBuf operations, this pointer may become
196 * invalid. So don't lean on it after altering the buffer!
197 * Since this operation is considered cheap, rather call it often than risking
198 * your pointer to become invalid!
199 * Str the string we want to get the c-string representation for
200 * @returns the Pointer to the Content. Don't mess with it!
202 inline const char *ChrPtr(const StrBuf *Str) {
210 * since we know strlen()'s result, provide it here.
211 * Str the string to return the length to
212 * @returns contentlength of the buffer
214 inline int StrLength(const StrBuf *Str) {
215 return (Str != NULL) ? Str->BufUsed : 0;
219 // local utility function to resize the buffer
220 // Buf the buffer whichs storage we should increase
221 // KeepOriginal should we copy the original buffer or just start over with a new one
222 // DestSize what should fit in after?
223 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
225 size_t NewSize = Buf->BufSize * 2;
232 while ((NewSize <= DestSize) && (NewSize != 0)) {
241 NewBuf = (char*) malloc(NewSize);
242 if (NewBuf == NULL) {
246 if (KeepOriginal && (Buf->BufUsed > 0)) {
247 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
255 Buf->BufSize = NewSize;
257 dbg_IncreaseBuf(Buf);
263 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
264 // Buf Buffer to shrink (has to be empty)
265 // ThreshHold if the buffer is bigger then this, its readjusted
266 // NewSize if we Shrink it, how big are we going to be afterwards?
267 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
268 if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
270 Buf->buf = (char*) malloc(NewSize);
272 Buf->BufSize = NewSize;
278 * shrink long term buffers to their real size so they don't waste memory
279 * Buf buffer to shrink
280 * Force if not set, will just executed if the buffer is much to big; set for lifetime strings
281 * @returns physical size of the buffer
283 long StrBufShrinkToFit(StrBuf *Buf, int Force) {
286 if (Force || (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize)) {
289 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
293 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
294 Buf->BufSize = Buf->BufUsed + 1;
303 * Allocate a new buffer with default buffer size
304 * @returns the new stringbuffer
306 StrBuf *NewStrBuf(void) {
309 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
313 NewBuf->buf = (char*) malloc(BaseStrBufSize);
314 if (NewBuf->buf == NULL) {
318 NewBuf->buf[0] = '\0';
319 NewBuf->BufSize = BaseStrBufSize;
321 NewBuf->ConstBuf = 0;
329 * Copy Constructor; returns a duplicate of CopyMe
330 * CopyMe Buffer to faxmilate
331 * @returns the new stringbuffer
333 StrBuf *NewStrBufDup(const StrBuf *CopyMe) {
339 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
343 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
344 if (NewBuf->buf == NULL) {
349 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
350 NewBuf->BufUsed = CopyMe->BufUsed;
351 NewBuf->BufSize = CopyMe->BufSize;
352 NewBuf->ConstBuf = 0;
361 * Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
362 * NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
363 * CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
364 * CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
365 * KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
366 * @returns the new stringbuffer
368 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal) {
371 if (CreateRelpaceMe == NULL)
375 if (*CreateRelpaceMe != NULL)
376 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
378 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
382 if (CopyFlushMe == NULL) {
383 if (*CreateRelpaceMe != NULL)
384 FlushStrBuf(*CreateRelpaceMe);
386 *CreateRelpaceMe = NewStrBuf();
391 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
392 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
393 * be a big IO-Buffer...
395 if (KeepOriginal || (StrLength(CopyFlushMe) < 256)) {
396 if (*CreateRelpaceMe == NULL) {
397 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
401 NewBuf = *CreateRelpaceMe;
404 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
407 if (*CreateRelpaceMe == NULL) {
408 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
412 NewBuf = *CreateRelpaceMe;
414 iSwapBuffers (NewBuf, CopyFlushMe);
417 FlushStrBuf(CopyFlushMe);
424 * create a new Buffer using an existing c-string
425 * this function should also be used if you want to pre-suggest
426 * the buffer size to allocate in conjunction with ptr == NULL
427 * ptr the c-string to copy; may be NULL to create a blank instance
428 * nChars How many chars should we copy; -1 if we should measure the length ourselves
429 * @returns the new stringbuffer
431 StrBuf *NewStrBufPlain(const char* ptr, int nChars) {
433 size_t Siz = BaseStrBufSize;
436 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
441 CopySize = strlen((ptr != NULL)?ptr:"");
445 while ((Siz <= CopySize) && (Siz != 0))
453 NewBuf->buf = (char*) malloc(Siz);
454 if (NewBuf->buf == NULL) {
458 NewBuf->BufSize = Siz;
460 memcpy(NewBuf->buf, ptr, CopySize);
461 NewBuf->buf[CopySize] = '\0';
462 NewBuf->BufUsed = CopySize;
465 NewBuf->buf[0] = '\0';
468 NewBuf->ConstBuf = 0;
477 * Set an existing buffer from a c-string
479 * ptr c-string to put into
480 * nChars set to -1 if we should work 0-terminated
481 * @returns the new length of the string
483 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars) {
497 CopySize = strlen(ptr);
501 while ((Siz <= CopySize) && (Siz != 0))
509 if (Siz != Buf->BufSize)
510 IncreaseBuf(Buf, 0, Siz);
511 memcpy(Buf->buf, ptr, CopySize);
512 Buf->buf[CopySize] = '\0';
513 Buf->BufUsed = CopySize;
520 * use strbuf as wrapper for a string constant for easy handling
521 * StringConstant a string to wrap
522 * SizeOfStrConstant should be sizeof(StringConstant)-1
524 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
528 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
531 NewBuf->buf = (char*) StringConstant;
532 NewBuf->BufSize = SizeOfStrConstant;
533 NewBuf->BufUsed = SizeOfStrConstant;
534 NewBuf->ConstBuf = 1;
543 * flush the content of a Buf; keep its struct
544 * buf Buffer to flush
546 int FlushStrBuf(StrBuf *buf)
548 if ((buf == NULL) || (buf->buf == NULL))
558 * wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
561 int FLUSHStrBuf(StrBuf *buf)
567 if (buf->BufUsed > 0) {
568 memset(buf->buf, 0, buf->BufUsed);
575 int hFreeDbglog = -1;
579 * Its a double pointer, so it can NULL your pointer
580 * so fancy SIG11 appear instead of random results
581 * FreeMe Pointer Pointer to the buffer to free
583 void FreeStrBuf (StrBuf **FreeMe)
588 dbg_FreeStrBuf(FreeMe, 'F');
590 if (!(*FreeMe)->ConstBuf)
591 free((*FreeMe)->buf);
597 * flatten a Buffer to the Char * we return
598 * Its a double pointer, so it can NULL your pointer
599 * so fancy SIG11 appear instead of random results
600 * The Callee then owns the buffer and is responsible for freeing it.
601 * SmashMe Pointer Pointer to the buffer to release Buf from and free
602 * @returns the pointer of the buffer; Callee owns the memory thereafter.
604 char *SmashStrBuf (StrBuf **SmashMe) {
607 if ((SmashMe == NULL) || (*SmashMe == NULL))
610 dbg_FreeStrBuf(SmashMe, 'S');
612 Ret = (*SmashMe)->buf;
621 * If you want put your StrBuf into a Hash, use this as Destructor.
622 * VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
624 void HFreeStrBuf (void *VFreeMe) {
625 StrBuf *FreeMe = (StrBuf*)VFreeMe;
629 dbg_FreeStrBuf(SmashMe, 'H');
631 if (!FreeMe->ConstBuf)
637 /*******************************************************************************
638 * Simple string transformations *
639 *******************************************************************************/
641 // Wrapper around atol
642 long StrTol(const StrBuf *Buf) {
646 return atol(Buf->buf);
652 // Wrapper around atoi
653 int StrToi(const StrBuf *Buf) {
656 if (Buf->BufUsed > 0)
657 return atoi(Buf->buf);
663 // Checks to see if the string is a pure number
664 // Buf The buffer to inspect
665 // returns 1 if its a pure number, 0, if not.
666 int StrBufIsNumber(const StrBuf *Buf) {
668 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
671 strtoll(Buf->buf, &pEnd, 10);
672 if (pEnd == Buf->buf)
674 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
676 if (Buf->buf == pEnd)
682 // modifies a Single char of the Buf
683 // You can point to it via char* or a zero-based integer
684 // Buf The buffer to manipulate
685 // ptr char* to zero; use NULL if unused
686 // nThChar zero based pointer into the string; use -1 if unused
687 // PeekValue The Character to place into the position
688 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue) {
692 nThChar = ptr - Buf->buf;
693 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
695 Buf->buf[nThChar] = PeekValue;
700 // modifies a range of chars of the Buf
701 // You can point to it via char* or a zero-based integer
702 // Buf The buffer to manipulate
703 // ptr char* to zero; use NULL if unused
704 // nThChar zero based pointer into the string; use -1 if unused
705 // nChars how many chars are to be flushed?
706 // PookValue The Character to place into that area
707 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue) {
711 nThChar = ptr - Buf->buf;
712 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
714 if (nThChar + nChars > Buf->BufUsed)
715 nChars = Buf->BufUsed - nThChar;
717 memset(Buf->buf + nThChar, PookValue, nChars);
718 // just to be sure...
719 Buf->buf[Buf->BufUsed] = 0;
724 // Append a StringBuffer to the buffer
725 // Buf Buffer to modify
726 // AppendBuf Buffer to copy at the end of our buffer
727 // Offset Should we start copying from an offset?
728 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset) {
729 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
730 (Buf == NULL) || (Buf->buf == NULL))
733 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
734 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
736 memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
737 Buf->BufUsed += AppendBuf->BufUsed - Offset;
738 Buf->buf[Buf->BufUsed] = '\0';
742 // Append a C-String to the buffer
743 // Buf Buffer to modify
744 // AppendBuf Buffer to copy at the end of our buffer
745 // AppendSize number of bytes to copy; set to -1 if we should count it in advance
746 // Offset Should we start copying from an offset?
747 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
749 long BufSizeRequired;
751 if ((AppendBuf == NULL) || (Buf == NULL))
754 if (AppendSize < 0) {
755 aps = strlen(AppendBuf + Offset);
758 aps = AppendSize - Offset;
761 BufSizeRequired = Buf->BufUsed + aps + 1;
762 if (Buf->BufSize <= BufSizeRequired) {
763 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
766 memcpy(Buf->buf + Buf->BufUsed,
770 Buf->buf[Buf->BufUsed] = '\0';
774 // sprintf like function appending the formated string to the buffer
775 // vsnprintf version to wrap into own calls
776 // Buf Buffer to extend by format and Params
777 // format printf alike format to add
778 // ap va_list containing the items for format
779 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap) {
786 if ((Buf == NULL) || (format == NULL))
789 BufSize = Buf->BufSize;
790 nWritten = Buf->BufSize + 1;
791 Offset = Buf->BufUsed;
792 newused = Offset + nWritten;
794 while (newused >= BufSize) {
796 nWritten = vsnprintf(Buf->buf + Offset, Buf->BufSize - Offset, format, apl);
798 newused = Offset + nWritten;
799 if (newused >= Buf->BufSize) {
800 if (IncreaseBuf(Buf, 1, newused) == -1)
801 return; // TODO: error handling?
802 newused = Buf->BufSize + 1;
805 Buf->BufUsed = Offset + nWritten;
806 BufSize = Buf->BufSize;
813 // sprintf like function appending the formated string to the buffer
814 // Buf Buffer to extend by format and Params
815 // format printf alike format to add
816 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...) {
823 if ((Buf == NULL) || (format == NULL))
826 BufSize = Buf->BufSize;
827 nWritten = Buf->BufSize + 1;
828 Offset = Buf->BufUsed;
829 newused = Offset + nWritten;
831 while (newused >= BufSize) {
832 va_start(arg_ptr, format);
833 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, Buf->BufSize - Buf->BufUsed, format, arg_ptr);
835 newused = Buf->BufUsed + nWritten;
836 if (newused >= Buf->BufSize) {
837 if (IncreaseBuf(Buf, 1, newused) == -1)
838 return; // TODO: error handling?
839 newused = Buf->BufSize + 1;
842 Buf->BufUsed += nWritten;
843 BufSize = Buf->BufSize;
850 // sprintf like function putting the formated string into the buffer
851 // Buf Buffer to extend by format and Parameters
852 // format printf alike format to add
853 void StrBufPrintf(StrBuf *Buf, const char *format, ...) {
857 if ((Buf == NULL) || (format == NULL))
860 nWritten = Buf->BufSize + 1;
861 while (nWritten >= Buf->BufSize) {
862 va_start(arg_ptr, format);
863 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
865 if (nWritten >= Buf->BufSize) {
866 if (IncreaseBuf(Buf, 0, 0) == -1)
867 return; // TODO: error handling?
868 nWritten = Buf->BufSize + 1;
871 Buf->BufUsed = nWritten ;
876 // Callback for cURL to append the webserver reply to a buffer
877 // ptr, size, nmemb, and stream are pre-defined by the cURL API; see man 3 curl for more info
878 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
886 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
891 // extracts a substring from Source into dest
892 // dest buffer to place substring into
893 // Source string to copy substring from
894 // Offset chars to skip from start
895 // nChars number of chars to copy
896 // returns the number of chars copied; may be different from nChars due to the size of Source
897 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars) {
899 if (Offset > Source->BufUsed) {
904 if (Offset + nChars < Source->BufUsed) {
905 if ((nChars >= dest->BufSize) &&
906 (IncreaseBuf(dest, 0, nChars + 1) == -1))
908 memcpy(dest->buf, Source->buf + Offset, nChars);
909 dest->BufUsed = nChars;
910 dest->buf[dest->BufUsed] = '\0';
913 NCharsRemain = Source->BufUsed - Offset;
914 if ((NCharsRemain >= dest->BufSize) &&
915 (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
917 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
918 dest->BufUsed = NCharsRemain;
919 dest->buf[dest->BufUsed] = '\0';
924 * Cut nChars from the start of the string
925 * Buf Buffer to modify
926 * nChars how many chars should be skipped?
928 void StrBufCutLeft(StrBuf *Buf, int nChars)
930 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
931 if (nChars >= Buf->BufUsed) {
935 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
936 Buf->BufUsed -= nChars;
937 Buf->buf[Buf->BufUsed] = '\0';
941 * Cut the trailing n Chars from the string
942 * Buf Buffer to modify
943 * nChars how many chars should be trunkated?
945 void StrBufCutRight(StrBuf *Buf, int nChars)
947 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
950 if (nChars >= Buf->BufUsed) {
954 Buf->BufUsed -= nChars;
955 Buf->buf[Buf->BufUsed] = '\0';
959 * Cut the string after n Chars
960 * Buf Buffer to modify
961 * AfternChars after how many chars should we trunkate the string?
962 * At if non-null and points inside of our string, cut it there.
964 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
966 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
968 AfternChars = At - Buf->buf;
971 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
973 Buf->BufUsed = AfternChars;
974 Buf->buf[Buf->BufUsed] = '\0';
979 * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
980 * Buf the string to modify
982 void StrBufTrim(StrBuf *Buf)
985 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
987 while ((Buf->BufUsed > 0) &&
988 isspace(Buf->buf[Buf->BufUsed - 1]))
992 Buf->buf[Buf->BufUsed] = '\0';
994 if (Buf->BufUsed == 0) return;
996 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
999 if (delta > 0) StrBufCutLeft(Buf, delta);
1002 * changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1003 * Buf the string to modify
1005 void StrBufSpaceToBlank(StrBuf *Buf)
1009 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1012 pche = pch + Buf->BufUsed;
1021 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1026 if ((Buf == NULL) || (Buf->buf == NULL)) {
1030 pRight = strchr(Buf->buf, rightboundary);
1031 if (pRight != NULL) {
1032 StrBufCutAt(Buf, 0, pRight);
1035 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1036 if (pLeft != NULL) {
1037 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1043 * uppercase the contents of a buffer
1044 * Buf the buffer to translate
1046 void StrBufUpCase(StrBuf *Buf)
1050 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1053 pche = pch + Buf->BufUsed;
1054 while (pch < pche) {
1055 *pch = toupper(*pch);
1062 * lowercase the contents of a buffer
1063 * Buf the buffer to translate
1065 void StrBufLowerCase(StrBuf *Buf)
1069 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1072 pche = pch + Buf->BufUsed;
1073 while (pch < pche) {
1074 *pch = tolower(*pch);
1080 /*******************************************************************************
1081 * a tokenizer that kills, maims, and destroys *
1082 *******************************************************************************/
1085 * Replace a token at a given place with a given length by another token with given length
1086 * Buf String where to work on
1087 * where where inside of the Buf is the search-token
1088 * HowLong How long is the token to be replaced
1089 * Repl Token to insert at 'where'
1090 * ReplLen Length of repl
1091 * @returns -1 if fail else length of resulting Buf
1093 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong,
1094 const char *Repl, long ReplLen)
1097 if ((Buf == NULL) ||
1098 (where > Buf->BufUsed) ||
1099 (where + HowLong > Buf->BufUsed))
1102 if (where + ReplLen - HowLong > Buf->BufSize)
1103 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1106 memmove(Buf->buf + where + ReplLen,
1107 Buf->buf + where + HowLong,
1108 Buf->BufUsed - where - HowLong);
1110 memcpy(Buf->buf + where,
1113 Buf->BufUsed += ReplLen - HowLong;
1115 return Buf->BufUsed;
1119 * Counts the numbmer of tokens in a buffer
1120 * source String to count tokens in
1121 * tok Tokenizer char to count
1122 * @returns numbers of tokenizer chars found
1124 int StrBufNum_tokens(const StrBuf *source, char tok)
1128 if ((source == NULL) || (source->BufUsed == 0))
1130 if ((source->BufUsed == 1) && (*source->buf == tok))
1134 pche = pch + source->BufUsed;
1145 * a string tokenizer
1146 * Source StringBuffer to read into
1147 * parmnum n'th Parameter to remove
1148 * separator tokenizer character
1149 * @returns -1 if not found, else length of token.
1151 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1154 char *d, *s, *end; /* dest, source */
1157 /* Find desired eter */
1158 end = Source->buf + Source->BufUsed;
1160 while ((d <= end) &&
1163 /* End of string, bail! */
1168 if (*d == separator) {
1173 if ((d == NULL) || (d >= end))
1174 return 0; /* @Parameter not found */
1176 /* Find next eter */
1178 while ((s <= end) &&
1179 (*s && *s != separator))
1183 if (*s == separator)
1187 /* Hack and slash */
1192 memmove(d, s, Source->BufUsed - (s - Source->buf));
1193 Source->BufUsed += ReducedBy;
1194 Source->buf[Source->BufUsed] = '\0';
1196 else if (d == Source->buf) {
1198 Source->BufUsed = 0;
1202 Source->BufUsed += ReducedBy;
1213 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1215 const StrBuf Temp = {
1228 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1232 * a string tokenizer
1233 * dest Destination StringBuffer
1234 * Source StringBuffer to read into
1235 * parmnum n'th Parameter to extract
1236 * separator tokenizer character
1237 * @returns -1 if not found, else length of token.
1239 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1241 const char *s, *e; //* source * /
1242 int len = 0; //* running total length of extracted string * /
1243 int current_token = 0; //* token currently being processed * /
1246 dest->buf[0] = '\0';
1252 if ((Source == NULL) || (Source->BufUsed ==0)) {
1256 e = s + Source->BufUsed;
1259 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1261 while ((s < e) && !IsEmptyStr(s)) {
1262 if (*s == separator) {
1265 if (len >= dest->BufSize) {
1266 dest->BufUsed = len;
1267 if (IncreaseBuf(dest, 1, -1) < 0) {
1272 if ( (current_token == parmnum) &&
1273 (*s != separator)) {
1274 dest->buf[len] = *s;
1277 else if (current_token > parmnum) {
1283 dest->buf[len] = '\0';
1284 dest->BufUsed = len;
1286 if (current_token < parmnum) {
1287 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1290 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1299 * a string tokenizer to fetch an integer
1300 * Source String containing tokens
1301 * parmnum n'th Parameter to extract
1302 * separator tokenizer character
1303 * @returns 0 if not found, else integer representation of the token
1305 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1315 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1322 * a string tokenizer to fetch a long integer
1323 * Source String containing tokens
1324 * parmnum n'th Parameter to extract
1325 * separator tokenizer character
1326 * @returns 0 if not found, else long integer representation of the token
1328 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1338 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1346 * a string tokenizer to fetch an unsigned long
1347 * Source String containing tokens
1348 * parmnum n'th Parameter to extract
1349 * separator tokenizer character
1350 * @returns 0 if not found, else unsigned long representation of the token
1352 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1363 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1367 return (unsigned long) atol(pnum);
1376 * a string tokenizer; Bounds checker
1377 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1378 * Source our tokenbuffer
1379 * pStart the token iterator pointer to inspect
1380 * @returns whether the revolving pointer is inside of the search range
1382 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1384 if ((Source == NULL) ||
1385 (*pStart == StrBufNOTNULL) ||
1386 (Source->BufUsed == 0))
1390 if (*pStart == NULL)
1394 else if (*pStart > Source->buf + Source->BufUsed)
1398 else if (*pStart <= Source->buf)
1407 * a string tokenizer
1408 * dest Destination StringBuffer
1409 * Source StringBuffer to read into
1410 * pStart pointer to the end of the last token. Feed with NULL on start.
1411 * separator tokenizer
1412 * @returns -1 if not found, else length of token.
1414 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1416 const char *s; /* source */
1417 const char *EndBuffer; /* end stop of source buffer */
1418 int current_token = 0; /* token currently being processed */
1419 int len = 0; /* running total length of extracted string */
1421 if ((Source == NULL) ||
1422 (Source->BufUsed == 0) )
1424 *pStart = StrBufNOTNULL;
1430 EndBuffer = Source->buf + Source->BufUsed;
1434 dest->buf[0] = '\0';
1439 *pStart = EndBuffer + 1;
1443 if (*pStart == NULL)
1445 *pStart = Source->buf; /* we're starting to examine this buffer. */
1447 else if ((*pStart < Source->buf) ||
1448 (*pStart > EndBuffer ) )
1450 return -1; /* no more tokens to find. */
1454 /* start to find the next token */
1455 while ((s <= EndBuffer) &&
1456 (current_token == 0) )
1458 if (*s == separator)
1460 /* we found the next token */
1464 if (len >= dest->BufSize)
1466 /* our Dest-buffer isn't big enough, increase it. */
1467 dest->BufUsed = len;
1469 if (IncreaseBuf(dest, 1, -1) < 0) {
1470 /* WHUT? no more mem? bail out. */
1477 if ( (current_token == 0 ) && /* are we in our target token? */
1478 (!IsEmptyStr(s) ) &&
1479 (separator != *s) ) /* don't copy the token itself */
1481 dest->buf[len] = *s; /* Copy the payload */
1482 ++len; /* remember the bigger size. */
1488 /* did we reach the end? */
1489 if ((s > EndBuffer)) {
1490 EndBuffer = StrBufNOTNULL;
1491 *pStart = EndBuffer;
1494 *pStart = s; /* remember the position for the next run */
1497 /* sanitize our extracted token */
1498 dest->buf[len] = '\0';
1499 dest->BufUsed = len;
1506 * a string tokenizer
1507 * Source StringBuffer to read from
1508 * pStart pointer to the end of the last token. Feed with NULL.
1509 * separator tokenizer character
1510 * nTokens number of tokens to fastforward over
1511 * @returns -1 if not found, else length of token.
1513 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1515 const char *s, *EndBuffer; //* source * /
1516 int len = 0; //* running total length of extracted string * /
1517 int current_token = 0; //* token currently being processed * /
1519 if ((Source == NULL) ||
1520 (Source->BufUsed ==0)) {
1524 return Source->BufUsed;
1526 if (*pStart == NULL)
1527 *pStart = Source->buf;
1529 EndBuffer = Source->buf + Source->BufUsed;
1531 if ((*pStart < Source->buf) ||
1532 (*pStart > EndBuffer)) {
1540 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1542 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1543 if (*s == separator) {
1546 if (current_token >= nTokens) {
1558 * a string tokenizer to fetch an integer
1559 * Source StringBuffer to read from
1560 * pStart Cursor on the tokenstring
1561 * separator tokenizer character
1562 * @returns 0 if not found, else integer representation of the token
1564 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1574 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1581 * a string tokenizer to fetch a long integer
1582 * Source StringBuffer to read from
1583 * pStart Cursor on the tokenstring
1584 * separator tokenizer character
1585 * @returns 0 if not found, else long integer representation of the token
1587 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1597 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1605 * a string tokenizer to fetch an unsigned long
1606 * Source StringBuffer to read from
1607 * pStart Cursor on the tokenstring
1608 * separator tokenizer character
1609 * @returns 0 if not found, else unsigned long representation of the token
1611 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1622 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1626 return (unsigned long) atol(pnum);
1636 /*******************************************************************************
1637 * Escape Appending *
1638 *******************************************************************************/
1641 * Escape a string for feeding out as a URL while appending it to a Buffer
1642 * OutBuf the output buffer
1643 * In Buffer to encode
1644 * PlainIn way in from plain old c strings
1646 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1648 const char *pch, *pche;
1652 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1654 if (PlainIn != NULL) {
1655 len = strlen(PlainIn);
1661 pche = pch + In->BufUsed;
1668 pt = OutBuf->buf + OutBuf->BufUsed;
1669 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1671 while (pch < pche) {
1673 IncreaseBuf(OutBuf, 1, -1);
1674 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1675 pt = OutBuf->buf + OutBuf->BufUsed;
1678 if((*pch >= 'a' && *pch <= 'z') ||
1679 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1680 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1681 (*pch == '!') || (*pch == '_') ||
1682 (*pch == ',') || (*pch == '.'))
1689 *(pt + 1) = HexList[(unsigned char)*pch][0];
1690 *(pt + 2) = HexList[(unsigned char)*pch][1];
1692 OutBuf->BufUsed += 3;
1701 * Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1702 * OutBuf the output buffer
1703 * In Buffer to encode
1704 * PlainIn way in from plain old c strings
1706 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1707 const char *pch, *pche;
1711 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1713 if (PlainIn != NULL) {
1714 len = strlen(PlainIn);
1720 pche = pch + In->BufUsed;
1727 pt = OutBuf->buf + OutBuf->BufUsed;
1728 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1730 while (pch < pche) {
1732 IncreaseBuf(OutBuf, 1, -1);
1733 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1734 pt = OutBuf->buf + OutBuf->BufUsed;
1737 if((*pch >= 'a' && *pch <= 'z') ||
1738 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1739 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1740 (*pch == '!') || (*pch == '_') ||
1741 (*pch == ',') || (*pch == '.'))
1748 *(pt + 1) = HexList[(unsigned char)*pch][0];
1749 *(pt + 2) = HexList[(unsigned char)*pch][1];
1751 OutBuf->BufUsed += 3;
1759 * append a string with characters having a special meaning in xml encoded to the buffer
1760 * OutBuf the output buffer
1761 * In Buffer to encode
1762 * PlainIn way in from plain old c strings
1763 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1764 * OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1766 void StrBufXMLEscAppend(StrBuf *OutBuf,
1768 const char *PlainIn,
1770 int OverrideLowChars)
1772 const char *pch, *pche;
1777 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1779 if (PlainIn != NULL) {
1781 len = strlen((const char*)PlainIn);
1788 pch = (const char*)In->buf;
1789 pche = pch + In->BufUsed;
1796 pt = OutBuf->buf + OutBuf->BufUsed;
1797 /**< we max append 6 chars at once plus the \0 */
1798 pte = OutBuf->buf + OutBuf->BufSize - 6;
1800 while (pch < pche) {
1802 OutBuf->BufUsed = pt - OutBuf->buf;
1803 IncreaseBuf(OutBuf, 1, -1);
1804 pte = OutBuf->buf + OutBuf->BufSize - 6;
1805 /**< we max append 3 chars at once plus the \0 */
1807 pt = OutBuf->buf + OutBuf->BufUsed;
1811 memcpy(pt, HKEY("<"));
1815 else if (*pch == '>') {
1816 memcpy(pt, HKEY(">"));
1820 else if (*pch == '&') {
1821 memcpy(pt, HKEY("&"));
1825 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
1830 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
1833 while ((IsUtf8Sequence > 0) &&
1846 *pt = HexList[*(unsigned char*)pch][0];
1848 *pt = HexList[*(unsigned char*)pch][1];
1857 OutBuf->BufUsed = pt - OutBuf->buf;
1862 * append a string in hex encoding to the buffer
1863 * OutBuf the output buffer
1864 * In Buffer to encode
1865 * PlainIn way in from plain old c strings
1866 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1868 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1870 const unsigned char *pch, *pche;
1874 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1876 if (PlainIn != NULL) {
1878 len = strlen((const char*)PlainIn);
1885 pch = (const unsigned char*)In->buf;
1886 pche = pch + In->BufUsed;
1893 pt = OutBuf->buf + OutBuf->BufUsed;
1894 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1896 while (pch < pche) {
1898 IncreaseBuf(OutBuf, 1, -1);
1899 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1900 pt = OutBuf->buf + OutBuf->BufUsed;
1903 *pt = HexList[*pch][0];
1905 *pt = HexList[*pch][1];
1906 pt ++; pch ++; OutBuf->BufUsed += 2;
1912 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks) {
1918 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1920 if (PlainIn != NULL) {
1922 len = strlen(PlainIn);
1935 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
1937 if (ExpectLen > OutBuf->BufSize)
1938 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
1941 pt = OutBuf->buf + OutBuf->BufUsed;
1943 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
1946 OutBuf->BufUsed += len;
1951 // append a string in hex encoding to the buffer
1952 // OutBuf the output buffer
1953 // In Buffer to encode
1954 // PlainIn way in from plain old c strings
1955 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1956 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1960 * Append a string, escaping characters which have meaning in HTML.
1962 * Target target buffer
1963 * Source source buffer; set to NULL if you just have a C-String
1964 * PlainIn Plain-C string to append; set to NULL if unused
1965 * nbsp If nonzero, spaces are converted to non-breaking spaces.
1966 * nolinebreaks if set to 1, linebreaks are removed from the string.
1967 * if set to 2, linebreaks are replaced by <br/>
1969 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1971 const char *aptr, *eiptr;
1975 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1978 if (PlainIn != NULL) {
1980 len = strlen(PlainIn);
1985 eiptr = aptr + Source->BufUsed;
1986 len = Source->BufUsed;
1992 bptr = Target->buf + Target->BufUsed;
1993 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1995 while (aptr < eiptr){
1997 IncreaseBuf(Target, 1, -1);
1998 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1999 bptr = Target->buf + Target->BufUsed;
2002 memcpy(bptr, "<", 4);
2004 Target->BufUsed += 4;
2006 else if (*aptr == '>') {
2007 memcpy(bptr, ">", 4);
2009 Target->BufUsed += 4;
2011 else if (*aptr == '&') {
2012 memcpy(bptr, "&", 5);
2014 Target->BufUsed += 5;
2016 else if (*aptr == '"') {
2017 memcpy(bptr, """, 6);
2019 Target->BufUsed += 6;
2021 else if (*aptr == '\'') {
2022 memcpy(bptr, "'", 5);
2024 Target->BufUsed += 5;
2026 else if (*aptr == LB) {
2031 else if (*aptr == RB) {
2036 else if (*aptr == QU) {
2041 else if ((*aptr == 32) && (nbsp == 1)) {
2042 memcpy(bptr, " ", 6);
2044 Target->BufUsed += 6;
2046 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2047 *bptr='\0'; /* nothing */
2049 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2050 memcpy(bptr, "<br/>", 11);
2052 Target->BufUsed += 11;
2056 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2057 *bptr='\0'; /* nothing */
2067 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2069 return Target->BufUsed;
2073 * Append a string, escaping characters which have meaning in HTML.
2074 * Converts linebreaks into blanks; escapes single quotes
2075 * Target target buffer
2076 * Source source buffer; set to NULL if you just have a C-String
2077 * PlainIn Plain-C string to append; set to NULL if unused
2079 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2081 const char *aptr, *eiptr;
2085 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2088 if (PlainIn != NULL) {
2090 len = strlen(PlainIn);
2095 eiptr = aptr + Source->BufUsed;
2096 len = Source->BufUsed;
2102 eptr = Target->buf + Target->BufSize - 8;
2103 tptr = Target->buf + Target->BufUsed;
2105 while (aptr < eiptr){
2107 IncreaseBuf(Target, 1, -1);
2108 eptr = Target->buf + Target->BufSize - 8;
2109 tptr = Target->buf + Target->BufUsed;
2112 if (*aptr == '\n') {
2116 else if (*aptr == '\r') {
2120 else if (*aptr == '\'') {
2126 Target->BufUsed += 5;
2139 * Append a string, escaping characters which have meaning in ICAL.
2141 * Target target buffer
2142 * Source source buffer; set to NULL if you just have a C-String
2143 * PlainIn Plain-C string to append; set to NULL if unused
2145 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2147 const char *aptr, *eiptr;
2151 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2154 if (PlainIn != NULL) {
2156 len = strlen(PlainIn);
2161 eiptr = aptr + Source->BufUsed;
2162 len = Source->BufUsed;
2168 eptr = Target->buf + Target->BufSize - 8;
2169 tptr = Target->buf + Target->BufUsed;
2171 while (aptr < eiptr){
2172 if(tptr + 3 >= eptr) {
2173 IncreaseBuf(Target, 1, -1);
2174 eptr = Target->buf + Target->BufSize - 8;
2175 tptr = Target->buf + Target->BufUsed;
2178 if (*aptr == '\n') {
2185 else if (*aptr == '\r') {
2192 else if (*aptr == ',') {
2208 * Append a string, escaping characters which have meaning in JavaScript strings .
2210 * Target target buffer
2211 * Source source buffer; set to NULL if you just have a C-String
2212 * PlainIn Plain-C string to append; set to NULL if unused
2213 * @returns size of result or -1
2215 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2217 const char *aptr, *eiptr;
2222 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2225 if (PlainIn != NULL) {
2227 len = strlen(PlainIn);
2232 eiptr = aptr + Source->BufUsed;
2233 len = Source->BufUsed;
2239 bptr = Target->buf + Target->BufUsed;
2240 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2242 while (aptr < eiptr){
2244 IncreaseBuf(Target, 1, -1);
2245 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2246 bptr = Target->buf + Target->BufUsed;
2250 memcpy(bptr, HKEY("\\n"));
2252 Target->BufUsed += 2;
2255 memcpy(bptr, HKEY("\\r"));
2257 Target->BufUsed += 2;
2264 Target->BufUsed += 2;
2267 if ((*(aptr + 1) == 'u') &&
2268 isxdigit(*(aptr + 2)) &&
2269 isxdigit(*(aptr + 3)) &&
2270 isxdigit(*(aptr + 4)) &&
2271 isxdigit(*(aptr + 5)))
2272 { /* oh, a unicode escaper. let it pass through. */
2273 memcpy(bptr, aptr, 6);
2276 Target->BufUsed += 6;
2284 Target->BufUsed += 2;
2292 Target->BufUsed += 2;
2299 Target->BufUsed += 2;
2306 Target->BufUsed += 2;
2309 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2310 while (IsUtf8Sequence > 0){
2313 if (--IsUtf8Sequence)
2321 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2323 return Target->BufUsed;
2327 * Append a string, escaping characters which have meaning in HTML + json.
2329 * Target target buffer
2330 * Source source buffer; set to NULL if you just have a C-String
2331 * PlainIn Plain-C string to append; set to NULL if unused
2332 * nbsp If nonzero, spaces are converted to non-breaking spaces.
2333 * nolinebreaks if set to 1, linebreaks are removed from the string.
2334 * if set to 2, linebreaks are replaced by <br/>
2336 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2338 const char *aptr, *eiptr;
2341 int IsUtf8Sequence = 0;
2343 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2346 if (PlainIn != NULL) {
2348 len = strlen(PlainIn);
2353 eiptr = aptr + Source->BufUsed;
2354 len = Source->BufUsed;
2360 bptr = Target->buf + Target->BufUsed;
2361 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2363 while (aptr < eiptr){
2365 IncreaseBuf(Target, 1, -1);
2366 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
2367 bptr = Target->buf + Target->BufUsed;
2371 memcpy(bptr, HKEY("<"));
2373 Target->BufUsed += 4;
2376 memcpy(bptr, HKEY(">"));
2378 Target->BufUsed += 4;
2381 memcpy(bptr, HKEY("&"));
2383 Target->BufUsed += 5;
2396 switch (nolinebreaks) {
2398 *bptr='\0'; /* nothing */
2401 memcpy(bptr, HKEY("<br/>"));
2403 Target->BufUsed += 11;
2406 memcpy(bptr, HKEY("\\n"));
2408 Target->BufUsed += 2;
2412 switch (nolinebreaks) {
2415 *bptr='\0'; /* nothing */
2418 memcpy(bptr, HKEY("\\r"));
2420 Target->BufUsed += 2;
2430 Target->BufUsed += 2;
2433 if ((*(aptr + 1) == 'u') &&
2434 isxdigit(*(aptr + 2)) &&
2435 isxdigit(*(aptr + 3)) &&
2436 isxdigit(*(aptr + 4)) &&
2437 isxdigit(*(aptr + 5)))
2438 { /* oh, a unicode escaper. let it pass through. */
2439 memcpy(bptr, aptr, 6);
2442 Target->BufUsed += 6;
2450 Target->BufUsed += 2;
2458 Target->BufUsed += 2;
2465 Target->BufUsed += 2;
2472 Target->BufUsed += 2;
2476 memcpy(bptr, HKEY(" "));
2478 Target->BufUsed += 6;
2482 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2483 while (IsUtf8Sequence > 0){
2486 if (--IsUtf8Sequence)
2494 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2496 return Target->BufUsed;
2501 * replace all non-Ascii characters by another
2502 * Buf buffer to inspect
2503 * repl charater to stamp over non ascii chars
2505 void StrBufAsciify(StrBuf *Buf, const char repl) {
2508 for (offset = 0; offset < Buf->BufUsed; offset ++)
2509 if (!isascii(Buf->buf[offset]))
2510 Buf->buf[offset] = repl;
2515 * unhide special chars hidden to the HTML escaper
2516 * target buffer to put the unescaped string in
2517 * source buffer to unescape
2519 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) {
2523 if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2528 FlushStrBuf(target);
2530 len = source->BufUsed;
2531 for (a = 0; a < len; ++a) {
2532 if (target->BufUsed >= target->BufSize)
2533 IncreaseBuf(target, 1, -1);
2535 if (source->buf[a] == '=') {
2536 hex[0] = source->buf[a + 1];
2537 hex[1] = source->buf[a + 2];
2540 sscanf(hex, "%02x", &b);
2541 target->buf[target->BufUsed] = b;
2542 target->buf[++target->BufUsed] = 0;
2546 target->buf[target->BufUsed] = source->buf[a];
2547 target->buf[++target->BufUsed] = 0;
2554 * hide special chars from the HTML escapers and friends
2555 * target buffer to put the escaped string in
2556 * source buffer to escape
2558 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) {
2562 FlushStrBuf(target);
2564 if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2569 len = source->BufUsed;
2570 for (i=0; i<len; ++i) {
2571 if (target->BufUsed + 4 >= target->BufSize)
2572 IncreaseBuf(target, 1, -1);
2573 if ( (isalnum(source->buf[i])) ||
2574 (source->buf[i]=='-') ||
2575 (source->buf[i]=='_') ) {
2576 target->buf[target->BufUsed++] = source->buf[i];
2579 sprintf(&target->buf[target->BufUsed],
2581 (0xFF &source->buf[i]));
2582 target->BufUsed += 3;
2585 target->buf[target->BufUsed + 1] = '\0';
2589 /*******************************************************************************
2590 * Quoted Printable de/encoding *
2591 *******************************************************************************/
2594 * decode a buffer from base 64 encoding; destroys original
2595 * Buf Buffor to transform
2597 int StrBufDecodeBase64(StrBuf *Buf) {
2605 xferbuf = (char*) malloc(Buf->BufSize);
2606 if (xferbuf == NULL)
2610 siz = CtdlDecodeBase64(xferbuf, Buf->buf, Buf->BufUsed);
2615 Buf->buf[Buf->BufUsed] = '\0';
2621 * decode a buffer from base 64 encoding; expects targetbuffer
2622 * BufIn Buffor to transform
2623 * BufOut Buffer to put result into
2625 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut) {
2626 if ((BufIn == NULL) || (BufOut == NULL))
2629 if (BufOut->BufSize < BufIn->BufUsed) {
2630 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2633 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf, BufIn->buf, BufIn->BufUsed);
2634 return BufOut->BufUsed;
2637 typedef struct __z_enc_stream {
2643 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err) {
2644 //base64_decodestate *state;;
2649 //case eBase64Decode:
2650 //case eBase64Encode:
2651 //state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2652 //base64_init_decodestate(state);
2653 //return (vStreamT*) state;
2658 z_enc_stream *stream;
2661 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2662 memset(stream, 0, sizeof(z_enc_stream));
2663 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2664 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2666 err = inflateInit(&stream->zstream);
2669 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2673 return (vStreamT*) stream;
2678 z_enc_stream *stream;
2681 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2682 memset(stream, 0, sizeof(z_enc_stream));
2683 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2684 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2685 /* write gzip header */
2686 stream->OutBuf.BufUsed = snprintf
2687 (stream->OutBuf.buf,
2688 stream->OutBuf.BufSize,
2689 "%c%c%c%c%c%c%c%c%c%c",
2690 gz_magic[0], gz_magic[1], Z_DEFLATED,
2691 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2694 err = deflateInit2(&stream->zstream,
2695 ZLibCompressionRatio,
2699 Z_DEFAULT_STRATEGY);
2701 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2705 return (vStreamT*) stream;
2716 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err) {
2721 if ((vStream == NULL) || (*vStream==NULL)) {
2722 *Err = strerror(EINVAL);
2727 //case eBase64Encode:
2728 //case eBase64Decode:
2734 z_enc_stream *stream = (z_enc_stream *)*vStream;
2735 (void)inflateEnd(&stream->zstream);
2736 free(stream->OutBuf.buf);
2741 z_enc_stream *stream = (z_enc_stream *)*vStream;
2742 err = deflateEnd(&stream->zstream);
2747 free(stream->OutBuf.buf);
2758 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err) {
2762 //case eBase64Encode:
2767 //case eBase64Decode:
2774 z_enc_stream *stream = (z_enc_stream *)vStream;
2775 int org_outbuf_len = stream->OutBuf.BufUsed;
2777 unsigned int chunkavail;
2779 if (In->ReadWritePointer != NULL)
2781 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2782 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
2783 (In->ReadWritePointer - In->Buf->buf);
2787 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2788 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2791 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2792 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2794 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
2796 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
2798 if (Target && (LastChunk || (stream->OutBuf.BufUsed != org_outbuf_len))) {
2799 iSwapBuffers(Target->Buf, &stream->OutBuf);
2802 if (stream->zstream.avail_in == 0) {
2803 FlushStrBuf(In->Buf);
2804 In->ReadWritePointer = NULL;
2807 if (stream->zstream.avail_in < 64) {
2808 memmove(In->Buf->buf,
2809 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2810 stream->zstream.avail_in);
2812 In->Buf->BufUsed = stream->zstream.avail_in;
2813 In->Buf->buf[In->Buf->BufUsed] = '\0';
2816 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2819 rc = (LastChunk && (err != Z_FINISH));
2820 if (!rc && (err != Z_OK)) {
2827 z_enc_stream *stream = (z_enc_stream *)vStream;
2828 int org_outbuf_len = stream->zstream.total_out;
2831 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
2832 if (In->ReadWritePointer != NULL) {
2833 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2834 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - (In->ReadWritePointer - In->Buf->buf);
2837 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2838 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2842 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2843 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2845 err = inflate(&stream->zstream, Z_NO_FLUSH);
2847 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
2850 err = Z_DATA_ERROR; /* and fall through */
2855 (void)inflateEnd(&stream->zstream);
2859 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
2861 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
2863 if (stream->zstream.avail_in == 0) {
2864 FlushStrBuf(In->Buf);
2865 In->ReadWritePointer = NULL;
2868 if (stream->zstream.avail_in < 64) {
2869 memmove(In->Buf->buf,
2870 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2871 stream->zstream.avail_in);
2873 In->Buf->BufUsed = stream->zstream.avail_in;
2874 In->Buf->buf[In->Buf->BufUsed] = '\0';
2878 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2892 * decode a buffer from base 64 encoding; destroys original
2893 * Buf Buffor to transform
2895 int StrBufDecodeHex(StrBuf *Buf) {
2897 char *pch, *pche, *pchi;
2899 if (Buf == NULL) return -1;
2901 pch = pchi = Buf->buf;
2902 pche = pch + Buf->BufUsed;
2904 while (pchi < pche){
2905 ch = decode_hex(pchi);
2912 Buf->BufUsed = pch - Buf->buf;
2913 return Buf->BufUsed;
2917 * replace all chars >0x20 && < 0x7F with Mute
2918 * Mute char to put over invalid chars
2919 * Buf Buffor to transform
2921 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2925 if (Buf == NULL) return -1;
2926 pch = (unsigned char *)Buf->buf;
2927 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2928 if ((*pch < 0x20) || (*pch > 0x7F))
2932 return Buf->BufUsed;
2937 * remove escaped strings from i.e. the url string (like %20 for blanks)
2938 * Buf Buffer to translate
2939 * StripBlanks Reduce several blanks to one?
2941 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2950 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2951 Buf->buf[Buf->BufUsed - 1] = '\0';
2956 while (a < Buf->BufUsed) {
2957 if (Buf->buf[a] == '+')
2959 else if (Buf->buf[a] == '%') {
2960 /* don't let % chars through, rather truncate the input. */
2961 if (a + 2 > Buf->BufUsed) {
2966 hex[0] = Buf->buf[a + 1];
2967 hex[1] = Buf->buf[a + 2];
2970 sscanf(hex, "%02x", &b);
2971 Buf->buf[a] = (char) b;
2972 len = Buf->BufUsed - a - 2;
2974 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2986 * RFC2047-encode a header field if necessary.
2987 * If no non-ASCII characters are found, the string
2988 * will be copied verbatim without encoding.
2990 * target Target buffer.
2991 * source Source string to be encoded.
2992 * @returns encoded length; -1 if non success.
2994 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2996 const char headerStr[] = "=?UTF-8?Q?";
2997 int need_to_encode = 0;
3001 if ((source == NULL) ||
3005 while ((i < source->BufUsed) &&
3006 (!IsEmptyStr (&source->buf[i])) &&
3007 (need_to_encode == 0)) {
3008 if (((unsigned char) source->buf[i] < 32) ||
3009 ((unsigned char) source->buf[i] > 126)) {
3015 if (!need_to_encode) {
3016 if (*target == NULL) {
3017 *target = NewStrBufPlain(source->buf, source->BufUsed);
3020 FlushStrBuf(*target);
3021 StrBufAppendBuf(*target, source, 0);
3024 return (*target)->BufUsed;
3028 if (*target == NULL)
3029 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3030 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3031 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3032 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3033 (*target)->BufUsed = sizeof(headerStr) - 1;
3034 for (i=0; (i < source->BufUsed); ++i) {
3035 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3036 IncreaseBuf(*target, 1, 0);
3037 ch = (unsigned char) source->buf[i];
3046 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3047 (*target)->BufUsed += 3;
3051 (*target)->buf[(*target)->BufUsed] = '_';
3053 (*target)->buf[(*target)->BufUsed] = ch;
3054 (*target)->BufUsed++;
3058 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3059 IncreaseBuf(*target, 1, 0);
3061 (*target)->buf[(*target)->BufUsed++] = '?';
3062 (*target)->buf[(*target)->BufUsed++] = '=';
3063 (*target)->buf[(*target)->BufUsed] = '\0';
3064 return (*target)->BufUsed;;
3067 // Quoted-Printable encode a message; make it < 80 columns width.
3068 // source Source string to be encoded.
3069 // returns buffer with encoded message.
3070 StrBuf *StrBufQuotedPrintableEncode(const StrBuf *EncodeMe) {
3073 const char *ptr, *eptr;
3077 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3079 OEptr = OutBuf->buf + OutBuf->BufSize;
3080 ptr = EncodeMe->buf;
3081 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3084 while (ptr < eptr) {
3085 if (Optr + 4 >= OEptr) {
3087 Offset = Optr - OutBuf->buf;
3088 OutBuf->BufUsed = Optr - OutBuf->buf;
3089 IncreaseBuf(OutBuf, 1, 0);
3090 Optr = OutBuf->buf + Offset;
3091 OEptr = OutBuf->buf + OutBuf->BufSize;
3093 if (*ptr == '\r') { // ignore carriage returns
3096 else if (*ptr == '\n') { // hard line break
3097 memcpy(Optr, HKEY("=0A"));
3102 else if (( (*ptr >= 32) && (*ptr <= 60) ) || ( (*ptr >= 62) && (*ptr <= 126) )) {
3112 *Optr = HexList[ch][0];
3114 *Optr = HexList[ch][1];
3120 if (LinePos > 72) { // soft line break
3121 if (isspace(*(Optr - 1))) {
3126 *Optr = HexList[ch][0];
3128 *Optr = HexList[ch][1];
3140 OutBuf->BufUsed = Optr - OutBuf->buf;
3146 static void AddRecipient(StrBuf *Target, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3149 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3150 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3152 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3153 StrBufRFC2047encode(&EncBuf, UserName);
3154 StrBufAppendBuf(Target, EncBuf, 0);
3155 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3156 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3158 if (StrLength(EmailAddress) > 0){
3159 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3160 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3161 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3167 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3168 * \param Recp Source list of email recipients
3169 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3170 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3171 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3172 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3174 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3176 const char *pch, *pche;
3177 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3179 if ((Recp == NULL) || (StrLength(Recp) == 0))
3183 pche = pch + StrLength(Recp);
3185 if (!CheckEncode(pch, -1, pche))
3186 return NewStrBufDup(Recp);
3188 Target = NewStrBufPlain(NULL, StrLength(Recp));
3190 while ((pch != NULL) && (pch < pche))
3192 while (isspace(*pch)) pch++;
3193 UserEnd = EmailStart = EmailEnd = NULL;
3195 if ((*pch == '"') || (*pch == '\'')) {
3196 UserStart = pch + 1;
3198 UserEnd = strchr(UserStart, *pch);
3199 if (UserEnd == NULL)
3200 break; ///TODO: Userfeedback??
3201 EmailStart = UserEnd + 1;
3202 while (isspace(*EmailStart))
3204 if (UserEnd == UserStart) {
3205 UserStart = UserEnd = NULL;
3208 if (*EmailStart == '<') {
3210 EmailEnd = strchr(EmailStart, '>');
3211 if (EmailEnd == NULL)
3212 EmailEnd = strchr(EmailStart, ',');
3216 EmailEnd = strchr(EmailStart, ',');
3218 if (EmailEnd == NULL)
3225 EmailEnd = strchr(UserStart, ',');
3226 if (EmailEnd == NULL) {
3227 EmailEnd = strchr(pch, '>');
3229 if (EmailEnd != NULL) {
3239 while ((EmailEnd > UserStart) && !gt &&
3240 ((*EmailEnd == ',') ||
3241 (*EmailEnd == '>') ||
3242 (isspace(*EmailEnd))))
3244 if (*EmailEnd == '>')
3249 if (EmailEnd == UserStart)
3253 EmailStart = strchr(UserStart, '<');
3254 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3256 UserEnd = EmailStart;
3258 while ((UserEnd > UserStart) &&
3259 isspace (*(UserEnd - 1)))
3262 if (UserStart >= UserEnd)
3263 UserStart = UserEnd = NULL;
3265 else { /* this is a local recipient... no domain, just a realname */
3266 EmailStart = UserStart;
3267 At = strchr(EmailStart, '@');
3273 EmailStart = UserStart;
3279 if ((UserStart != NULL) && (UserEnd != NULL))
3280 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3281 else if ((UserStart != NULL) && (UserEnd == NULL))
3282 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3284 FlushStrBuf(UserName);
3286 if ((EmailStart != NULL) && (EmailEnd != NULL))
3287 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3288 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3289 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3291 FlushStrBuf(EmailAddress);
3293 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3298 if ((pch != NULL) && (*pch == ','))
3300 if (pch != NULL) while (isspace(*pch))
3308 * replaces all occurances of 'search' by 'replace'
3309 * buf Buffer to modify
3310 * search character to search
3311 * replace character to replace search by
3313 void StrBufReplaceChars(StrBuf *buf, char search, char replace) {
3317 for (i=0; i<buf->BufUsed; i++)
3318 if (buf->buf[i] == search)
3319 buf->buf[i] = replace;
3324 * removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3325 * buf Buffer to modify
3327 void StrBufToUnixLF(StrBuf *buf)
3329 char *pche, *pchS, *pchT;
3333 pche = buf->buf + buf->BufUsed;
3334 pchS = pchT = buf->buf;
3340 if (*pchS != '\n') {
3349 buf->BufUsed = pchT - buf->buf;
3353 /*******************************************************************************
3354 * Iconv Wrapper; RFC822 de/encoding *
3355 *******************************************************************************/
3358 * Wrapper around iconv_open()
3359 * Our version adds aliases for non-standard Microsoft charsets
3360 * such as 'MS950', aliasing them to names like 'CP950'
3362 * tocode Target encoding
3363 * fromcode Source encoding
3364 * pic anonimized pointer to iconv struct
3366 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3369 iconv_t ic = (iconv_t)(-1) ;
3370 ic = iconv_open(tocode, fromcode);
3371 if (ic == (iconv_t)(-1) ) {
3372 char alias_fromcode[64];
3373 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3374 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3375 alias_fromcode[0] = 'C';
3376 alias_fromcode[1] = 'P';
3377 ic = iconv_open(tocode, alias_fromcode);
3380 *(iconv_t *)pic = ic;
3386 * find one chunk of a RFC822 encoded string
3387 * Buffer where to search
3388 * bptr where to start searching
3389 * @returns found position, NULL if none.
3391 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3394 /* Find the next ?Q? */
3395 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3398 end = strchr(bptr + 2, '?');
3403 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3404 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3405 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3406 (*(end + 2) == '?')) {
3407 /* skip on to the end of the cluster, the next ?= */
3408 end = strstr(end + 3, "?=");
3411 /* sort of half valid encoding, try to find an end. */
3412 end = strstr(bptr, "?=");
3419 * convert one buffer according to the preselected iconv pointer PIC
3420 * ConvertBuf buffer we need to translate
3421 * TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3422 * pic Pointer to the iconv-session Object
3424 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3430 char *ibuf; /**< Buffer of characters to be converted */
3431 char *obuf; /**< Buffer for converted characters */
3432 size_t ibuflen; /**< Length of input buffer */
3433 size_t obuflen; /**< Length of output buffer */
3436 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3439 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3440 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3441 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3443 ic = *(iconv_t*)pic;
3444 ibuf = ConvertBuf->buf;
3445 ibuflen = ConvertBuf->BufUsed;
3447 obuflen = TmpBuf->BufSize;
3449 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3452 if (errno == E2BIG) {
3454 IncreaseBuf(TmpBuf, 0, 0);
3459 else if (errno == EILSEQ){
3460 /* hm, invalid utf8 sequence... what to do now? */
3461 /* An invalid multibyte sequence has been encountered in the input */
3463 else if (errno == EINVAL) {
3464 /* An incomplete multibyte sequence has been encountered in the input. */
3467 FlushStrBuf(TmpBuf);
3470 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3471 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3473 /* little card game: wheres the red lady? */
3474 iSwapBuffers(ConvertBuf, TmpBuf);
3475 FlushStrBuf(TmpBuf);
3482 * catches one RFC822 encoded segment, and decodes it.
3483 * Target buffer to fill with result
3484 * DecodeMe buffer with stuff to process
3485 * SegmentStart points to our current segment in DecodeMe
3486 * SegmentEnd Points to the end of our current segment in DecodeMe
3487 * ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3488 * ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3489 * FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3491 inline static void DecodeSegment(StrBuf *Target,
3492 const StrBuf *DecodeMe,
3493 const char *SegmentStart,
3494 const char *SegmentEnd,
3496 StrBuf *ConvertBuf2,
3497 StrBuf *FoundCharset)
3503 iconv_t ic = (iconv_t)(-1);
3507 /* Now we handle foreign character sets properly encoded
3508 * in RFC2047 format.
3510 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3511 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3512 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3513 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3514 if (FoundCharset != NULL) {
3515 FlushStrBuf(FoundCharset);
3516 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3518 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3519 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3521 *encoding = toupper(*encoding);
3522 if (*encoding == 'B') { /**< base64 */
3523 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3524 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3525 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3527 ConvertBuf->BufUsed);
3529 else if (*encoding == 'Q') { /**< quoted-printable */
3533 while (pos < ConvertBuf->BufUsed)
3535 if (ConvertBuf->buf[pos] == '_')
3536 ConvertBuf->buf[pos] = ' ';
3540 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3541 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3543 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3546 ConvertBuf->BufUsed);
3549 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3552 ctdl_iconv_open("UTF-8", charset, &ic);
3553 if (ic != (iconv_t)(-1) ) {
3555 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3556 StrBufAppendBuf(Target, ConvertBuf2, 0);
3561 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3567 * Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3568 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3569 * Target where to put the decoded string to
3570 * DecodeMe buffer with encoded string
3571 * DefaultCharset if we don't find one, which should we use?
3572 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3573 * put it here for later use where no string might be known.
3575 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3578 StrBuf *ConvertBuf2;
3579 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3580 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3582 StrBuf_RFC822_2_Utf8(Target,
3588 FreeStrBuf(&ConvertBuf);
3589 FreeStrBuf(&ConvertBuf2);
3593 * Handle subjects with RFC2047 encoding such as:
3594 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3595 * Target where to put the decoded string to
3596 * DecodeMe buffer with encoded string
3597 * DefaultCharset if we don't find one, which should we use?
3598 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3599 * put it here for later use where no string might be known.
3600 * ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3601 * ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3603 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3604 const StrBuf *DecodeMe,
3605 const StrBuf* DefaultCharset,
3606 StrBuf *FoundCharset,
3608 StrBuf *ConvertBuf2)
3610 StrBuf *DecodedInvalidBuf = NULL;
3611 const StrBuf *DecodeMee = DecodeMe;
3612 const char *start, *end, *next, *nextend, *ptr = NULL;
3614 iconv_t ic = (iconv_t)(-1) ;
3619 int illegal_non_rfc2047_encoding = 0;
3622 if (DecodeMe == NULL)
3624 /* Sometimes, badly formed messages contain strings which were simply
3625 * written out directly in some foreign character set instead of
3626 * using RFC2047 encoding. This is illegal but we will attempt to
3627 * handle it anyway by converting from a user-specified default
3628 * charset to UTF-8 if we see any nonprintable characters.
3631 for (i=0; i<DecodeMe->BufUsed; ++i) {
3632 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3633 illegal_non_rfc2047_encoding = 1;
3638 if ((illegal_non_rfc2047_encoding) &&
3639 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3640 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3643 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3644 if (ic != (iconv_t)(-1) ) {
3645 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3646 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3647 DecodeMee = DecodedInvalidBuf;
3653 /* pre evaluate the first pair */
3655 start = strstr(DecodeMee->buf, "=?");
3656 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3658 end = FindNextEnd (DecodeMee, start + 2);
3660 StrBufAppendBuf(Target, DecodeMee, 0);
3661 FreeStrBuf(&DecodedInvalidBuf);
3666 if (start != DecodeMee->buf) {
3669 nFront = start - DecodeMee->buf;
3670 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3673 * Since spammers will go to all sorts of absurd lengths to get their
3674 * messages through, there are LOTS of corrupt headers out there.
3675 * So, prevent a really badly formed RFC2047 header from throwing
3676 * this function into an infinite loop.
3678 while ((start != NULL) &&
3685 DecodeSegment(Target,
3693 next = strstr(end, "=?");
3695 if ((next != NULL) &&
3697 nextend = FindNextEnd(DecodeMee, next);
3698 if (nextend == NULL)
3701 /* did we find two partitions */
3702 if ((next != NULL) &&
3706 while ((ptr < next) &&
3713 * did we find a gab just filled with blanks?
3714 * if not, copy its stuff over.
3718 StrBufAppendBufPlain(Target,
3724 /* our next-pair is our new first pair now. */
3730 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3731 if ((end != NULL) && (end < nextend)) {
3733 while ( (ptr < nextend) &&
3740 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3742 FreeStrBuf(&DecodedInvalidBuf);
3745 /*******************************************************************************
3746 * Manipulating UTF-8 Strings *
3747 *******************************************************************************/
3750 * evaluate the length of an utf8 special character sequence
3751 * Char the character to examine
3752 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3754 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3757 unsigned char test = (1<<7);
3759 if ((*CharS & 0xC0) != 0xC0)
3763 ((test & ((unsigned char)*CharS)) != 0))
3768 if ((n > 6) || ((CharE - CharS) < n))
3774 * detect whether this char starts an utf-8 encoded char
3775 * Char character to inspect
3776 * @returns yes or no
3778 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3780 /** 11??.???? indicates an UTF8 Sequence. */
3781 return ((Char & 0xC0) == 0xC0);
3785 * measure the number of glyphs in an UTF8 string...
3786 * Buf string to measure
3787 * @returns the number of glyphs in Buf
3789 long StrBuf_Utf8StrLen(StrBuf *Buf)
3795 if ((Buf == NULL) || (Buf->BufUsed == 0))
3798 eptr = Buf->buf + Buf->BufUsed;
3799 while ((aptr < eptr) && (*aptr != '\0')) {
3800 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3801 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3802 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3814 * cuts a string after maxlen glyphs
3815 * Buf string to cut to maxlen glyphs
3816 * maxlen how long may the string become?
3817 * @returns current length of the string
3819 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3825 eptr = Buf->buf + Buf->BufUsed;
3826 while ((aptr < eptr) && (*aptr != '\0')) {
3827 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3828 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3829 while ((*aptr++ != '\0') && (m-- > 0));
3838 Buf->BufUsed = aptr - Buf->buf;
3839 return Buf->BufUsed;
3842 return Buf->BufUsed;
3850 /*******************************************************************************
3852 *******************************************************************************/
3855 * uses the same calling syntax as compress2(), but it
3856 * creates a stream compatible with HTTP "Content-encoding: gzip"
3857 * dest compressed buffer
3858 * destLen length of the compresed data
3859 * source source to encode
3860 * sourceLen length of source to encode
3861 * level compression level
3864 int ZEXPORT compress_gzip(Bytef * dest,
3866 const Bytef * source,
3870 /* write gzip header */
3871 snprintf((char *) dest, *destLen,
3872 "%c%c%c%c%c%c%c%c%c%c",
3873 gz_magic[0], gz_magic[1], Z_DEFLATED,
3874 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3877 /* normal deflate */
3880 stream.next_in = (Bytef *) source;
3881 stream.avail_in = (uInt) sourceLen;
3882 stream.next_out = dest + 10L; // after header
3883 stream.avail_out = (uInt) * destLen;
3884 if ((uLong) stream.avail_out != *destLen)
3887 stream.zalloc = (alloc_func) 0;
3888 stream.zfree = (free_func) 0;
3889 stream.opaque = (voidpf) 0;
3891 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3892 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3896 err = deflate(&stream, Z_FINISH);
3897 if (err != Z_STREAM_END) {
3898 deflateEnd(&stream);
3899 return err == Z_OK ? Z_BUF_ERROR : err;
3901 *destLen = stream.total_out + 10L;
3903 /* write CRC and Length */
3904 uLong crc = crc32(0L, source, sourceLen);
3906 for (n = 0; n < 4; ++n, ++*destLen) {
3907 dest[*destLen] = (int) (crc & 0xff);
3910 uLong len = stream.total_in;
3911 for (n = 0; n < 4; ++n, ++*destLen) {
3912 dest[*destLen] = (int) (len & 0xff);
3915 err = deflateEnd(&stream);
3922 * compress the buffer with gzip
3923 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3924 * Buf buffer whose content is to be gzipped
3926 int CompressBuffer(StrBuf *Buf)
3929 char *compressed_data = NULL;
3930 size_t compressed_len, bufsize;
3933 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3934 compressed_data = malloc(compressed_len);
3936 if (compressed_data == NULL)
3938 /* Flush some space after the used payload so valgrind shuts up... */
3939 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3940 Buf->buf[Buf->BufUsed + i++] = '\0';
3941 if (compress_gzip((Bytef *) compressed_data,
3944 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3947 Buf->buf = compressed_data;
3948 Buf->BufUsed = compressed_len;
3949 Buf->BufSize = bufsize;
3950 /* Flush some space after the used payload so valgrind shuts up... */
3952 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3953 Buf->buf[Buf->BufUsed + i++] = '\0';
3956 free(compressed_data);
3958 #endif /* HAVE_ZLIB */
3962 /*******************************************************************************
3963 * File I/O; Callbacks to libevent *
3964 *******************************************************************************/
3966 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3971 if ((FB == NULL) || (FB->Buf == NULL))
3975 * check whether the read pointer is somewhere in a range
3976 * where a cut left is inexpensive
3979 if (FB->ReadWritePointer != NULL)
3983 already_read = FB->ReadWritePointer - FB->Buf->buf;
3984 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3986 if (already_read != 0) {
3989 unread = FB->Buf->BufUsed - already_read;
3991 /* else nothing to compact... */
3993 FB->ReadWritePointer = FB->Buf->buf;
3994 bufremain = FB->Buf->BufSize;
3996 else if ((unread < 64) ||
3997 (bufremain < already_read))
4000 * if its just a tiny bit remaining, or we run out of space...
4003 FB->Buf->BufUsed = unread;
4004 if (unread < already_read)
4005 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4007 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4008 FB->ReadWritePointer = FB->Buf->buf;
4009 bufremain = FB->Buf->BufSize - unread - 1;
4011 else if (bufremain < (FB->Buf->BufSize / 10))
4013 /* get a bigger buffer */
4015 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4017 FB->ReadWritePointer = FB->Buf->buf + unread;
4019 bufremain = FB->Buf->BufSize - unread - 1;
4020 /*TODO: special increase function that won't copy the already read! */
4023 else if (bufremain < 10) {
4024 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4026 FB->ReadWritePointer = FB->Buf->buf;
4028 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4033 FB->ReadWritePointer = FB->Buf->buf;
4034 bufremain = FB->Buf->BufSize - 1;
4037 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4040 FB->Buf->BufUsed += n;
4041 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4046 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4051 if ((FB == NULL) || (FB->Buf == NULL))
4054 if (FB->ReadWritePointer != NULL)
4056 WriteRemain = FB->Buf->BufUsed -
4057 (FB->ReadWritePointer -
4061 FB->ReadWritePointer = FB->Buf->buf;
4062 WriteRemain = FB->Buf->BufUsed;
4065 n = write(fd, FB->ReadWritePointer, WriteRemain);
4067 FB->ReadWritePointer += n;
4069 if (FB->ReadWritePointer ==
4070 FB->Buf->buf + FB->Buf->BufUsed)
4072 FlushStrBuf(FB->Buf);
4073 FB->ReadWritePointer = NULL;
4076 // check whether we've got something to write
4077 // get the maximum chunk plus the pointer we can send
4078 // write whats there
4079 // if not all was sent, remember the send pointer for the next time
4080 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4086 * extract a "next line" from Buf; Ptr to persist across several iterations
4087 * LineBuf your line will be copied here.
4088 * FB BLOB with lines of text...
4089 * Ptr moved arround to keep the next-line across several iterations
4090 * has to be &NULL on start; will be &NotNULL on end of buffer
4091 * @returns size of copied buffer
4093 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4095 const char *aptr, *ptr, *eptr;
4098 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4102 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4103 FB->ReadWritePointer = StrBufNOTNULL;
4107 FlushStrBuf(LineBuf);
4108 if (FB->ReadWritePointer == NULL)
4109 ptr = aptr = FB->Buf->buf;
4111 ptr = aptr = FB->ReadWritePointer;
4113 optr = LineBuf->buf;
4114 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4115 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4117 while ((ptr <= eptr) &&
4124 LineBuf->BufUsed = optr - LineBuf->buf;
4125 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4126 optr = LineBuf->buf + LineBuf->BufUsed;
4127 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4132 if (optr > LineBuf->buf)
4134 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4135 LineBuf->BufUsed = optr - LineBuf->buf;
4137 if ((FB->ReadWritePointer != NULL) &&
4138 (FB->ReadWritePointer != FB->Buf->buf))
4140 /* Ok, the client application read all the data
4141 it was interested in so far. Since there is more to read,
4142 we now shrink the buffer, and move the rest over.
4144 StrBufCutLeft(FB->Buf,
4145 FB->ReadWritePointer - FB->Buf->buf);
4146 FB->ReadWritePointer = FB->Buf->buf;
4148 return eMustReadMore;
4151 LineBuf->BufUsed = optr - LineBuf->buf;
4153 if ((ptr <= eptr) && (*ptr == '\r'))
4155 if ((ptr <= eptr) && (*ptr == '\n'))
4159 FB->ReadWritePointer = ptr;
4162 FlushStrBuf(FB->Buf);
4163 FB->ReadWritePointer = NULL;
4166 return eReadSuccess;
4170 * check whether the chunk-buffer has more data waiting or not.
4171 * FB Chunk-Buffer to inspect
4173 eReadState StrBufCheckBuffer(IOBuffer *FB)
4177 if (FB->Buf->BufUsed == 0)
4178 return eReadSuccess;
4179 if (FB->ReadWritePointer == NULL)
4180 return eBufferNotEmpty;
4181 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4182 return eBufferNotEmpty;
4183 return eReadSuccess;
4186 long IOBufferStrLength(IOBuffer *FB)
4188 if ((FB == NULL) || (FB->Buf == NULL))
4190 if (FB->ReadWritePointer == NULL)
4191 return StrLength(FB->Buf);
4193 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4197 /*******************************************************************************
4198 * File I/O; Prefer buffered read since its faster! *
4199 *******************************************************************************/
4202 * Read a line from socket
4203 * flushes and closes the FD on error
4204 * buf the buffer to get the input to
4205 * fd pointer to the filedescriptor to read
4206 * append Append to an existing string or replace?
4207 * Error strerror() on error
4208 * @returns numbers of chars read
4210 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4212 int len, rlen, slen;
4214 if ((buf == NULL) || (buf->buf == NULL)) {
4215 *Error = strerror(EINVAL);
4222 slen = len = buf->BufUsed;
4224 rlen = read(*fd, &buf->buf[len], 1);
4226 *Error = strerror(errno);
4233 if (buf->buf[len] == '\n')
4235 if (buf->buf[len] != '\r')
4237 if (len + 2 >= buf->BufSize) {
4239 buf->buf[len+1] = '\0';
4240 IncreaseBuf(buf, 1, -1);
4244 buf->buf[len] = '\0';
4250 * Read a line from socket
4251 * flushes and closes the FD on error
4252 * Line the line to read from the fd / I/O Buffer
4253 * buf the buffer to get the input to
4254 * fd pointer to the filedescriptor to read
4255 * timeout number of successless selects until we bail out
4256 * selectresolution how long to wait on each select
4257 * Error strerror() on error
4258 * @returns numbers of chars read
4260 int StrBufTCP_read_buffered_line(StrBuf *Line,
4264 int selectresolution,
4268 int nSuccessLess = 0;
4275 if (buf->BufUsed > 0) {
4276 pch = strchr(buf->buf, '\n');
4279 len = pch - buf->buf;
4280 if (len > 0 && (*(pch - 1) == '\r') )
4282 StrBufSub(Line, buf, 0, len - rlen);
4283 StrBufCutLeft(buf, len + 1);
4288 if (buf->BufSize - buf->BufUsed < 10)
4289 IncreaseBuf(buf, 1, -1);
4291 fdflags = fcntl(*fd, F_GETFL);
4292 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4294 while ((nSuccessLess < timeout) && (pch == NULL)) {
4296 tv.tv_sec = selectresolution;
4301 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4302 *Error = strerror(errno);
4308 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4313 &buf->buf[buf->BufUsed],
4314 buf->BufSize - buf->BufUsed - 1);
4316 *Error = strerror(errno);
4321 else if (rlen > 0) {
4323 buf->BufUsed += rlen;
4324 buf->buf[buf->BufUsed] = '\0';
4325 pch = strchr(buf->buf, '\n');
4326 if ((pch == NULL) &&
4327 (buf->BufUsed + 10 > buf->BufSize) &&
4328 (IncreaseBuf(buf, 1, -1) == -1))
4336 len = pch - buf->buf;
4337 if (len > 0 && (*(pch - 1) == '\r') )
4339 StrBufSub(Line, buf, 0, len - rlen);
4340 StrBufCutLeft(buf, len + 1);
4347 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4348 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4349 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4351 * Read a line from socket
4352 * flushes and closes the FD on error
4353 * Line where to append our Line read from the fd / I/O Buffer;
4354 * IOBuf the buffer to get the input to; lifetime pair to FD
4355 * Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4356 * fd pointer to the filedescriptor to read
4357 * timeout number of successless selects until we bail out
4358 * selectresolution how long to wait on each select
4359 * Error strerror() on error
4360 * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4362 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4367 int selectresolution,
4370 const char *pche = NULL;
4371 const char *pos = NULL;
4373 int len, rlen, retlen;
4374 int nSuccessLess = 0;
4376 const char *pch = NULL;
4382 if ((Line == NULL) ||
4389 *Error = ErrRBLF_PreConditionFailed;
4394 if ((IOBuf->BufUsed > 0) &&
4396 (pos < IOBuf->buf + IOBuf->BufUsed))
4400 pche = IOBuf->buf + IOBuf->BufUsed;
4404 while ((pch < pche) && (*pch != '\n'))
4406 if (Line->BufUsed + 10 > Line->BufSize)
4409 apos = pcht - Line->buf;
4411 IncreaseBuf(Line, 1, -1);
4412 pcht = Line->buf + apos;
4420 if (len > 0 && (*(pch - 1) == '\r') )
4429 if ((pch >= pche) || (*pch == '\0'))
4437 if ((pch != NULL) &&
4440 if (pch + 1 >= pche) {
4453 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4455 if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4456 IncreaseBuf(IOBuf, 1, -1);
4458 fdflags = fcntl(*fd, F_GETFL);
4459 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4462 while ((nSuccessLess < timeout) &&
4472 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4473 *Error = strerror(errno);
4477 *Error = ErrRBLF_SelectFailed;
4480 if (! FD_ISSET(*fd, &rfds) != 0) {
4486 &IOBuf->buf[IOBuf->BufUsed],
4487 IOBuf->BufSize - IOBuf->BufUsed - 1);
4489 *Error = strerror(errno);
4494 else if (rlen > 0) {
4496 pLF = IOBuf->buf + IOBuf->BufUsed;
4497 IOBuf->BufUsed += rlen;
4498 IOBuf->buf[IOBuf->BufUsed] = '\0';
4500 pche = IOBuf->buf + IOBuf->BufUsed;
4502 while ((pLF < pche) && (*pLF != '\n'))
4504 if ((pLF >= pche) || (*pLF == '\0'))
4507 if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4511 if (pLF != NULL) apos = pLF - IOBuf->buf;
4512 IncreaseBuf(IOBuf, 1, -1);
4513 if (pLF != NULL) pLF = IOBuf->buf + apos;
4527 if (len > 0 && (*(pLF - 1) == '\r') )
4529 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4530 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4536 return retlen + len;
4538 *Error = ErrRBLF_NotEnoughSentFromServer;
4543 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4545 * Input binary data from socket
4546 * flushes and closes the FD on error
4547 * Buf the buffer to get the input to
4548 * fd pointer to the filedescriptor to read
4549 * append Append to an existing string or replace?
4550 * nBytes the maximal number of bytes to read
4551 * Error strerror() on error
4552 * @returns numbers of chars read
4554 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4565 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4567 *Error = ErrRBLF_BLOBPreConditionFailed;
4572 if (Buf->BufUsed + nBytes >= Buf->BufSize)
4573 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4575 ptr = Buf->buf + Buf->BufUsed;
4577 fdflags = fcntl(*fd, F_GETFL);
4578 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4580 while ((nRead < nBytes) &&
4590 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4591 *Error = strerror(errno);
4595 *Error = ErrRBLF_SelectFailed;
4598 if (! FD_ISSET(*fd, &rfds) != 0) {
4604 if ((rlen = read(*fd,
4606 nBytes - nRead)) == -1) {
4609 *Error = strerror(errno);
4614 Buf->BufUsed += rlen;
4616 Buf->buf[Buf->BufUsed] = '\0';
4620 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4621 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4623 * Input binary data from socket
4624 * flushes and closes the FD on error
4625 * Blob put binary thing here
4626 * IOBuf the buffer to get the input to
4627 * Pos offset inside of IOBuf
4628 * fd pointer to the filedescriptor to read
4629 * append Append to an existing string or replace?
4630 * nBytes the maximal number of bytes to read
4631 * check whether we should search for '000\n' terminators in case of timeouts
4632 * Error strerror() on error
4633 * @returns numbers of chars read
4635 int StrBufReadBLOBBuffered(StrBuf *Blob,
4648 int nAlreadyRead = 0;
4653 int nSuccessLess = 0;
4656 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL)) {
4659 *Error = ErrRBB_BLOBFPreConditionFailed;
4665 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4666 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4671 rlen = pos - IOBuf->buf;
4673 rlen = IOBuf->BufUsed - rlen;
4676 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed)) {
4677 if (rlen < nBytes) {
4678 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4679 Blob->BufUsed += rlen;
4680 Blob->buf[Blob->BufUsed] = '\0';
4681 nAlreadyRead = nRead = rlen;
4684 if (rlen >= nBytes) {
4685 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4686 Blob->BufUsed += nBytes;
4687 Blob->buf[Blob->BufUsed] = '\0';
4688 if (rlen == nBytes) {
4700 if (IOBuf->BufSize < nBytes - nRead) {
4701 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4705 fdflags = fcntl(*fd, F_GETFL);
4706 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4714 while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
4721 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4722 *Error = strerror(errno);
4725 if (*Error == NULL) {
4726 *Error = ErrRBLF_SelectFailed;
4730 if (! FD_ISSET(*fd, &rfds) != 0) {
4735 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
4736 if (rlen < 1) { // We will always get at least 1 byte unless the connection is broken
4739 *Error = strerror(errno);
4742 else if (rlen == 0){
4743 if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
4744 StrBufPlain(Blob, HKEY("\n000\n"));
4745 StrBufCutRight(Blob, 5);
4746 return Blob->BufUsed;
4748 else if (!IsNonBlock)
4750 else if (nSuccessLess > MaxTries) {
4752 *Error = ErrRBB_too_many_selects;
4756 else if (rlen > 0) {
4760 IOBuf->BufUsed += rlen;
4763 if (nSuccessLess >= MaxTries) {
4765 *Error = ErrRBB_too_many_selects;
4769 if (nRead > nBytes) {
4770 *Pos = IOBuf->buf + nBytes;
4772 Blob->buf[Blob->BufUsed] = '\0';
4773 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4777 return nRead + nAlreadyRead;
4781 // extract a "next line" from Buf; Ptr to persist across several iterations
4782 // LineBuf your line will be copied here.
4783 // Buf BLOB with lines of text...
4784 // Ptr moved arround to keep the next-line across several iterations
4785 // has to be &NULL on start; will be &NotNULL on end of buffer
4786 // returns size of remaining buffer
4787 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr) {
4788 const char *aptr, *ptr, *eptr;
4791 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL) || (LineBuf == NULL)|| (LineBuf->buf == NULL)) {
4792 *Ptr = StrBufNOTNULL;
4796 FlushStrBuf(LineBuf);
4798 ptr = aptr = Buf->buf;
4802 optr = LineBuf->buf;
4803 eptr = Buf->buf + Buf->BufUsed;
4804 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4806 while ((ptr <= eptr) && (*ptr != '\n') && (*ptr != '\r') ) {
4810 LineBuf->BufUsed = optr - LineBuf->buf;
4811 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4812 optr = LineBuf->buf + LineBuf->BufUsed;
4813 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4817 if ((ptr >= eptr) && (optr > LineBuf->buf))
4819 LineBuf->BufUsed = optr - LineBuf->buf;
4821 if ((ptr <= eptr) && (*ptr == '\r'))
4823 if ((ptr <= eptr) && (*ptr == '\n'))
4830 *Ptr = StrBufNOTNULL;
4833 return Buf->BufUsed - (ptr - Buf->buf);
4837 // removes double slashes from pathnames
4838 // Dir directory string to filter
4839 // RemoveTrailingSlash allows / disallows trailing slashes
4840 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash) {
4845 while (!IsEmptyStr(a)) {
4857 if ((RemoveTrailingSlash) &&
4863 Dir->BufUsed = b - Dir->buf;
4867 // Decode a quoted-printable encoded StrBuf buffer "in place"
4868 // This is possible because the decoded will always be shorter than the encoded
4869 // so we don't have to worry about the buffer being to small.
4870 void StrBufDecodeQP(StrBuf *Buf) {
4871 if (!Buf) { // sanity check #1
4875 int source_len = StrLength(Buf);
4876 if (source_len < 1) { // sanity check #2
4880 int spos = 0; // source position
4881 int tpos = 0; // target position
4883 while (spos < source_len) {
4884 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
4887 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
4890 else if (Buf->buf[spos] == '=') {
4893 sscanf(&Buf->buf[spos], "%02x", &ch);
4894 Buf->buf[tpos++] = ch;
4898 Buf->buf[tpos++] = Buf->buf[spos++];
4903 Buf->BufUsed = tpos;