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
134 n = write(hFreeDbglog, buf, n);
139 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
144 n = write(hFreeDbglog, buf, n);
149 void dbg_IncreaseBuf(StrBuf *IncMe) {
151 #ifdef HAVE_BACKTRACE
152 StrBufBacktrace(Buf, 1);
157 void dbg_Init(StrBuf *Buf) {
160 Buf->bt_lastinc[0] = '\0';
161 #ifdef HAVE_BACKTRACE
162 StrBufBacktrace(Buf, 0);
168 #define dbg_FreeStrBuf(a, b)
169 #define dbg_IncreaseBuf(a)
175 * swaps the contents of two StrBufs
176 * this is to be used to have cheap switched between a work-buffer and a target buffer
180 static inline void iSwapBuffers(StrBuf *A, StrBuf *B) {
183 memcpy(&C, A, sizeof(*A));
184 memcpy(A, B, sizeof(*B));
185 memcpy(B, &C, sizeof(C));
190 void SwapBuffers(StrBuf *A, StrBuf *B) {
196 * Cast operator to Plain String
197 * @note if the buffer is altered by StrBuf operations, this pointer may become
198 * invalid. So don't lean on it after altering the buffer!
199 * Since this operation is considered cheap, rather call it often than risking
200 * your pointer to become invalid!
201 * Str the string we want to get the c-string representation for
202 * @returns the Pointer to the Content. Don't mess with it!
204 inline const char *ChrPtr(const StrBuf *Str) {
212 * since we know strlen()'s result, provide it here.
213 * Str the string to return the length to
214 * @returns contentlength of the buffer
216 inline int StrLength(const StrBuf *Str) {
217 return (Str != NULL) ? Str->BufUsed : 0;
221 // local utility function to resize the buffer
222 // Buf the buffer whichs storage we should increase
223 // KeepOriginal should we copy the original buffer or just start over with a new one
224 // DestSize what should fit in after?
225 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
227 size_t NewSize = Buf->BufSize * 2;
234 while ((NewSize <= DestSize) && (NewSize != 0)) {
243 NewBuf = (char*) malloc(NewSize);
244 if (NewBuf == NULL) {
248 if (KeepOriginal && (Buf->BufUsed > 0)) {
249 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
257 Buf->BufSize = NewSize;
259 dbg_IncreaseBuf(Buf);
265 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
266 // Buf Buffer to shrink (has to be empty)
267 // ThreshHold if the buffer is bigger then this, its readjusted
268 // NewSize if we Shrink it, how big are we going to be afterwards?
269 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
270 if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
272 Buf->buf = (char*) malloc(NewSize);
274 Buf->BufSize = NewSize;
280 * shrink long term buffers to their real size so they don't waste memory
281 * Buf buffer to shrink
282 * Force if not set, will just executed if the buffer is much to big; set for lifetime strings
283 * @returns physical size of the buffer
285 long StrBufShrinkToFit(StrBuf *Buf, int Force) {
288 if (Force || (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize)) {
291 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
295 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
296 Buf->BufSize = Buf->BufUsed + 1;
305 * Allocate a new buffer with default buffer size
306 * @returns the new stringbuffer
308 StrBuf *NewStrBuf(void) {
311 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
315 NewBuf->buf = (char*) malloc(BaseStrBufSize);
316 if (NewBuf->buf == NULL) {
320 NewBuf->buf[0] = '\0';
321 NewBuf->BufSize = BaseStrBufSize;
323 NewBuf->ConstBuf = 0;
331 * Copy Constructor; returns a duplicate of CopyMe
332 * CopyMe Buffer to faxmilate
333 * @returns the new stringbuffer
335 StrBuf *NewStrBufDup(const StrBuf *CopyMe) {
341 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
345 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
346 if (NewBuf->buf == NULL) {
351 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
352 NewBuf->BufUsed = CopyMe->BufUsed;
353 NewBuf->BufSize = CopyMe->BufSize;
354 NewBuf->ConstBuf = 0;
363 * Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
364 * NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
365 * CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
366 * CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
367 * KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
368 * @returns the new stringbuffer
370 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal) {
373 if (CreateRelpaceMe == NULL)
377 if (*CreateRelpaceMe != NULL)
378 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
380 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
384 if (CopyFlushMe == NULL) {
385 if (*CreateRelpaceMe != NULL)
386 FlushStrBuf(*CreateRelpaceMe);
388 *CreateRelpaceMe = NewStrBuf();
393 * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
394 * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
395 * be a big IO-Buffer...
397 if (KeepOriginal || (StrLength(CopyFlushMe) < 256)) {
398 if (*CreateRelpaceMe == NULL) {
399 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
403 NewBuf = *CreateRelpaceMe;
406 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
409 if (*CreateRelpaceMe == NULL) {
410 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
414 NewBuf = *CreateRelpaceMe;
416 iSwapBuffers (NewBuf, CopyFlushMe);
419 FlushStrBuf(CopyFlushMe);
426 * create a new Buffer using an existing c-string
427 * this function should also be used if you want to pre-suggest
428 * the buffer size to allocate in conjunction with ptr == NULL
429 * ptr the c-string to copy; may be NULL to create a blank instance
430 * nChars How many chars should we copy; -1 if we should measure the length ourselves
431 * @returns the new stringbuffer
433 StrBuf *NewStrBufPlain(const char* ptr, int nChars) {
435 size_t Siz = BaseStrBufSize;
438 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
443 CopySize = strlen((ptr != NULL)?ptr:"");
447 while ((Siz <= CopySize) && (Siz != 0))
455 NewBuf->buf = (char*) malloc(Siz);
456 if (NewBuf->buf == NULL) {
460 NewBuf->BufSize = Siz;
462 memcpy(NewBuf->buf, ptr, CopySize);
463 NewBuf->buf[CopySize] = '\0';
464 NewBuf->BufUsed = CopySize;
467 NewBuf->buf[0] = '\0';
470 NewBuf->ConstBuf = 0;
479 * Set an existing buffer from a c-string
481 * ptr c-string to put into
482 * nChars set to -1 if we should work 0-terminated
483 * @returns the new length of the string
485 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars) {
499 CopySize = strlen(ptr);
503 while ((Siz <= CopySize) && (Siz != 0))
511 if (Siz != Buf->BufSize)
512 IncreaseBuf(Buf, 0, Siz);
513 memcpy(Buf->buf, ptr, CopySize);
514 Buf->buf[CopySize] = '\0';
515 Buf->BufUsed = CopySize;
522 * use strbuf as wrapper for a string constant for easy handling
523 * StringConstant a string to wrap
524 * SizeOfStrConstant should be sizeof(StringConstant)-1
526 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
530 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
533 NewBuf->buf = (char*) StringConstant;
534 NewBuf->BufSize = SizeOfStrConstant;
535 NewBuf->BufUsed = SizeOfStrConstant;
536 NewBuf->ConstBuf = 1;
545 * flush the content of a Buf; keep its struct
546 * buf Buffer to flush
548 int FlushStrBuf(StrBuf *buf) {
549 if ((buf == NULL) || (buf->buf == NULL)) {
561 * wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
564 int FLUSHStrBuf(StrBuf *buf)
570 if (buf->BufUsed > 0) {
571 memset(buf->buf, 0, buf->BufUsed);
578 int hFreeDbglog = -1;
582 * Its a double pointer, so it can NULL your pointer
583 * so fancy SIG11 appear instead of random results
584 * FreeMe Pointer Pointer to the buffer to free
586 void FreeStrBuf (StrBuf **FreeMe)
591 dbg_FreeStrBuf(FreeMe, 'F');
593 if (!(*FreeMe)->ConstBuf)
594 free((*FreeMe)->buf);
600 * flatten a Buffer to the Char * we return
601 * Its a double pointer, so it can NULL your pointer
602 * so fancy SIG11 appear instead of random results
603 * The Callee then owns the buffer and is responsible for freeing it.
604 * SmashMe Pointer Pointer to the buffer to release Buf from and free
605 * @returns the pointer of the buffer; Callee owns the memory thereafter.
607 char *SmashStrBuf (StrBuf **SmashMe) {
610 if ((SmashMe == NULL) || (*SmashMe == NULL))
613 dbg_FreeStrBuf(SmashMe, 'S');
615 Ret = (*SmashMe)->buf;
624 * If you want put your StrBuf into a Hash, use this as Destructor.
625 * VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
627 void HFreeStrBuf (void *VFreeMe) {
628 StrBuf *FreeMe = (StrBuf*)VFreeMe;
632 dbg_FreeStrBuf(SmashMe, 'H');
634 if (!FreeMe->ConstBuf)
640 /*******************************************************************************
641 * Simple string transformations *
642 *******************************************************************************/
644 // Wrapper around atol
645 long StrTol(const StrBuf *Buf) {
649 return atol(Buf->buf);
655 // Wrapper around atoi
656 int StrToi(const StrBuf *Buf) {
659 if (Buf->BufUsed > 0)
660 return atoi(Buf->buf);
666 // Checks to see if the string is a pure number
667 // Buf The buffer to inspect
668 // returns 1 if its a pure number, 0, if not.
669 int StrBufIsNumber(const StrBuf *Buf) {
671 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
674 strtoll(Buf->buf, &pEnd, 10);
675 if (pEnd == Buf->buf)
677 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
679 if (Buf->buf == pEnd)
685 // modifies a Single char of the Buf
686 // You can point to it via char* or a zero-based integer
687 // Buf The buffer to manipulate
688 // ptr char* to zero; use NULL if unused
689 // nThChar zero based pointer into the string; use -1 if unused
690 // PeekValue The Character to place into the position
691 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue) {
695 nThChar = ptr - Buf->buf;
696 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
698 Buf->buf[nThChar] = PeekValue;
703 // modifies a range of chars of the Buf
704 // You can point to it via char* or a zero-based integer
705 // Buf The buffer to manipulate
706 // ptr char* to zero; use NULL if unused
707 // nThChar zero based pointer into the string; use -1 if unused
708 // nChars how many chars are to be flushed?
709 // PookValue The Character to place into that area
710 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue) {
714 nThChar = ptr - Buf->buf;
715 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
717 if (nThChar + nChars > Buf->BufUsed)
718 nChars = Buf->BufUsed - nThChar;
720 memset(Buf->buf + nThChar, PookValue, nChars);
721 // just to be sure...
722 Buf->buf[Buf->BufUsed] = 0;
727 // Append a StringBuffer to the buffer
728 // Buf Buffer to modify
729 // AppendBuf Buffer to copy at the end of our buffer
730 // Offset Should we start copying from an offset?
731 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset) {
732 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) || (Buf == NULL) || (Buf->buf == NULL)) {
736 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
737 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
739 memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
740 Buf->BufUsed += AppendBuf->BufUsed - Offset;
741 Buf->buf[Buf->BufUsed] = '\0';
745 // Append a C-String to the buffer
746 // Buf Buffer to modify
747 // AppendBuf Buffer to copy at the end of our buffer
748 // AppendSize number of bytes to copy; set to -1 if we should count it in advance
749 // Offset Should we start copying from an offset?
750 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
752 long BufSizeRequired;
754 if ((AppendBuf == NULL) || (Buf == NULL))
757 if (AppendSize < 0) {
758 aps = strlen(AppendBuf + Offset);
761 aps = AppendSize - Offset;
764 BufSizeRequired = Buf->BufUsed + aps + 1;
765 if (Buf->BufSize <= BufSizeRequired) {
766 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
769 memcpy(Buf->buf + Buf->BufUsed, AppendBuf + Offset, aps);
771 Buf->buf[Buf->BufUsed] = '\0';
775 // sprintf like function appending the formated string to the buffer
776 // vsnprintf version to wrap into own calls
777 // Buf Buffer to extend by format and Params
778 // format printf alike format to add
779 // ap va_list containing the items for format
780 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap) {
787 if ((Buf == NULL) || (format == NULL))
790 BufSize = Buf->BufSize;
791 nWritten = Buf->BufSize + 1;
792 Offset = Buf->BufUsed;
793 newused = Offset + nWritten;
795 while (newused >= BufSize) {
797 nWritten = vsnprintf(Buf->buf + Offset, Buf->BufSize - Offset, format, apl);
799 newused = Offset + nWritten;
800 if (newused >= Buf->BufSize) {
801 if (IncreaseBuf(Buf, 1, newused) == -1)
802 return; // TODO: error handling?
803 newused = Buf->BufSize + 1;
806 Buf->BufUsed = Offset + nWritten;
807 BufSize = Buf->BufSize;
814 // sprintf like function appending the formated string to the buffer
815 // Buf Buffer to extend by format and Params
816 // format printf alike format to add
817 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...) {
824 if ((Buf == NULL) || (format == NULL))
827 BufSize = Buf->BufSize;
828 nWritten = Buf->BufSize + 1;
829 Offset = Buf->BufUsed;
830 newused = Offset + nWritten;
832 while (newused >= BufSize) {
833 va_start(arg_ptr, format);
834 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, Buf->BufSize - Buf->BufUsed, format, arg_ptr);
836 newused = Buf->BufUsed + nWritten;
837 if (newused >= Buf->BufSize) {
838 if (IncreaseBuf(Buf, 1, newused) == -1)
839 return; // TODO: error handling?
840 newused = Buf->BufSize + 1;
843 Buf->BufUsed += nWritten;
844 BufSize = Buf->BufSize;
851 // sprintf like function putting the formated string into the buffer
852 // Buf Buffer to extend by format and Parameters
853 // format printf alike format to add
854 void StrBufPrintf(StrBuf *Buf, const char *format, ...) {
858 if ((Buf == NULL) || (format == NULL))
861 nWritten = Buf->BufSize + 1;
862 while (nWritten >= Buf->BufSize) {
863 va_start(arg_ptr, format);
864 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
866 if (nWritten >= Buf->BufSize) {
867 if (IncreaseBuf(Buf, 0, 0) == -1)
868 return; // TODO: error handling?
869 nWritten = Buf->BufSize + 1;
872 Buf->BufUsed = nWritten ;
877 // Callback for cURL to append the webserver reply to a buffer
878 // ptr, size, nmemb, and stream are pre-defined by the cURL API; see man 3 curl for more info
879 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
887 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
892 // extracts a substring from Source into dest
893 // dest buffer to place substring into
894 // Source string to copy substring from
895 // Offset chars to skip from start
896 // nChars number of chars to copy
897 // returns the number of chars copied; may be different from nChars due to the size of Source
898 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars) {
900 if (Offset > Source->BufUsed) {
905 if (Offset + nChars < Source->BufUsed) {
906 if ((nChars >= dest->BufSize) && (IncreaseBuf(dest, 0, nChars + 1) == -1)) {
909 memcpy(dest->buf, Source->buf + Offset, nChars);
910 dest->BufUsed = nChars;
911 dest->buf[dest->BufUsed] = '\0';
914 NCharsRemain = Source->BufUsed - Offset;
915 if ((NCharsRemain >= dest->BufSize) && (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1)) {
918 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
919 dest->BufUsed = NCharsRemain;
920 dest->buf[dest->BufUsed] = '\0';
925 * Cut nChars from the start of the string
926 * Buf Buffer to modify
927 * nChars how many chars should be skipped?
929 void StrBufCutLeft(StrBuf *Buf, int nChars)
931 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
932 if (nChars >= Buf->BufUsed) {
936 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
937 Buf->BufUsed -= nChars;
938 Buf->buf[Buf->BufUsed] = '\0';
942 * Cut the trailing n Chars from the string
943 * Buf Buffer to modify
944 * nChars how many chars should be trunkated?
946 void StrBufCutRight(StrBuf *Buf, int nChars)
948 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
951 if (nChars >= Buf->BufUsed) {
955 Buf->BufUsed -= nChars;
956 Buf->buf[Buf->BufUsed] = '\0';
960 * Cut the string after n Chars
961 * Buf Buffer to modify
962 * AfternChars after how many chars should we trunkate the string?
963 * At if non-null and points inside of our string, cut it there.
965 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
967 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
969 AfternChars = At - Buf->buf;
972 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
974 Buf->BufUsed = AfternChars;
975 Buf->buf[Buf->BufUsed] = '\0';
980 * Strip leading and trailing spaces from a string; with premeasured and adjusted length.
981 * Buf the string to modify
983 void StrBufTrim(StrBuf *Buf)
986 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
988 while ((Buf->BufUsed > 0) && isspace(Buf->buf[Buf->BufUsed - 1])) {
991 Buf->buf[Buf->BufUsed] = '\0';
993 if (Buf->BufUsed == 0) return;
995 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
998 if (delta > 0) StrBufCutLeft(Buf, delta);
1001 * changes all spaces in the string (tab, linefeed...) to Blank (0x20)
1002 * Buf the string to modify
1004 void StrBufSpaceToBlank(StrBuf *Buf)
1008 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1011 pche = pch + Buf->BufUsed;
1020 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1025 if ((Buf == NULL) || (Buf->buf == NULL)) {
1029 pRight = strchr(Buf->buf, rightboundary);
1030 if (pRight != NULL) {
1031 StrBufCutAt(Buf, 0, pRight);
1034 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1035 if (pLeft != NULL) {
1036 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1042 * uppercase the contents of a buffer
1043 * Buf the buffer to translate
1045 void StrBufUpCase(StrBuf *Buf)
1049 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1052 pche = pch + Buf->BufUsed;
1053 while (pch < pche) {
1054 *pch = toupper(*pch);
1061 * lowercase the contents of a buffer
1062 * Buf the buffer to translate
1064 void StrBufLowerCase(StrBuf *Buf)
1068 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1071 pche = pch + Buf->BufUsed;
1072 while (pch < pche) {
1073 *pch = tolower(*pch);
1079 /*******************************************************************************
1080 * a tokenizer that kills, maims, and destroys *
1081 *******************************************************************************/
1084 * Replace a token at a given place with a given length by another token with given length
1085 * Buf String where to work on
1086 * where where inside of the Buf is the search-token
1087 * HowLong How long is the token to be replaced
1088 * Repl Token to insert at 'where'
1089 * ReplLen Length of repl
1090 * @returns -1 if fail else length of resulting Buf
1092 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, const char *Repl, long ReplLen) {
1094 if ((Buf == NULL) || (where > Buf->BufUsed) || (where + HowLong > Buf->BufUsed)) {
1098 if (where + ReplLen - HowLong > Buf->BufSize) {
1099 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0) {
1104 memmove(Buf->buf + where + ReplLen, Buf->buf + where + HowLong, Buf->BufUsed - where - HowLong);
1105 memcpy(Buf->buf + where, Repl, ReplLen);
1106 Buf->BufUsed += ReplLen - HowLong;
1107 return Buf->BufUsed;
1111 * Counts the numbmer of tokens in a buffer
1112 * source String to count tokens in
1113 * tok Tokenizer char to count
1114 * @returns numbers of tokenizer chars found
1116 int StrBufNum_tokens(const StrBuf *source, char tok) {
1119 if ((source == NULL) || (source->BufUsed == 0))
1121 if ((source->BufUsed == 1) && (*source->buf == tok))
1125 pche = pch + source->BufUsed;
1136 * a string tokenizer
1137 * Source StringBuffer to read into
1138 * parmnum n'th Parameter to remove
1139 * separator tokenizer character
1140 * @returns -1 if not found, else length of token.
1142 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1145 char *d, *s, *end; /* dest, source */
1148 /* Find desired eter */
1149 end = Source->buf + Source->BufUsed;
1151 while ((d <= end) && (count < parmnum)) {
1152 /* End of string, bail! */
1157 if (*d == separator) {
1162 if ((d == NULL) || (d >= end))
1163 return 0; /* @Parameter not found */
1165 /* Find next eter */
1167 while ((s <= end) && (*s && *s != separator)) {
1170 if (*s == separator)
1174 /* Hack and slash */
1179 memmove(d, s, Source->BufUsed - (s - Source->buf));
1180 Source->BufUsed += ReducedBy;
1181 Source->buf[Source->BufUsed] = '\0';
1183 else if (d == Source->buf) {
1185 Source->BufUsed = 0;
1189 Source->BufUsed += ReducedBy;
1200 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1202 const StrBuf Temp = {
1215 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1219 * a string tokenizer
1220 * dest Destination StringBuffer
1221 * Source StringBuffer to read into
1222 * parmnum n'th Parameter to extract
1223 * separator tokenizer character
1224 * @returns -1 if not found, else length of token.
1226 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1228 const char *s, *e; //* source * /
1229 int len = 0; //* running total length of extracted string * /
1230 int current_token = 0; //* token currently being processed * /
1233 dest->buf[0] = '\0';
1239 if ((Source == NULL) || (Source->BufUsed ==0)) {
1243 e = s + Source->BufUsed;
1246 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1248 while ((s < e) && !IsEmptyStr(s)) {
1249 if (*s == separator) {
1252 if (len >= dest->BufSize) {
1253 dest->BufUsed = len;
1254 if (IncreaseBuf(dest, 1, -1) < 0) {
1259 if ( (current_token == parmnum) && (*s != separator)) {
1260 dest->buf[len] = *s;
1263 else if (current_token > parmnum) {
1269 dest->buf[len] = '\0';
1270 dest->BufUsed = len;
1272 if (current_token < parmnum) {
1273 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1276 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1285 * a string tokenizer to fetch an integer
1286 * Source String containing tokens
1287 * parmnum n'th Parameter to extract
1288 * separator tokenizer character
1289 * @returns 0 if not found, else integer representation of the token
1291 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1301 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1308 * a string tokenizer to fetch a long integer
1309 * Source String containing tokens
1310 * parmnum n'th Parameter to extract
1311 * separator tokenizer character
1312 * @returns 0 if not found, else long integer representation of the token
1314 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1324 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1332 * a string tokenizer to fetch an unsigned long
1333 * Source String containing tokens
1334 * parmnum n'th Parameter to extract
1335 * separator tokenizer character
1336 * @returns 0 if not found, else unsigned long representation of the token
1338 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1349 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1353 return (unsigned long) atol(pnum);
1362 * a string tokenizer; Bounds checker
1363 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1364 * Source our tokenbuffer
1365 * pStart the token iterator pointer to inspect
1366 * @returns whether the revolving pointer is inside of the search range
1368 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart) {
1369 if ((Source == NULL) || (*pStart == StrBufNOTNULL) || (Source->BufUsed == 0)) {
1372 if (*pStart == NULL) {
1375 else if (*pStart > Source->buf + Source->BufUsed) {
1378 else if (*pStart <= Source->buf) {
1386 * a string tokenizer
1387 * dest Destination StringBuffer
1388 * Source StringBuffer to read into
1389 * pStart pointer to the end of the last token. Feed with NULL on start.
1390 * separator tokenizer
1391 * @returns -1 if not found, else length of token.
1393 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator) {
1394 const char *s; /* source */
1395 const char *EndBuffer; /* end stop of source buffer */
1396 int current_token = 0; /* token currently being processed */
1397 int len = 0; /* running total length of extracted string */
1399 if ((Source == NULL) || (Source->BufUsed == 0)) {
1400 *pStart = StrBufNOTNULL;
1407 EndBuffer = Source->buf + Source->BufUsed;
1410 dest->buf[0] = '\0';
1414 *pStart = EndBuffer + 1;
1418 if (*pStart == NULL) {
1419 *pStart = Source->buf; /* we're starting to examine this buffer. */
1421 else if ((*pStart < Source->buf) || (*pStart > EndBuffer ) ) {
1422 return -1; /* no more tokens to find. */
1426 /* start to find the next token */
1427 while ((s <= EndBuffer) && (current_token == 0) ) {
1428 if (*s == separator) {
1429 /* we found the next token */
1433 if (len >= dest->BufSize) {
1434 /* our Dest-buffer isn't big enough, increase it. */
1435 dest->BufUsed = len;
1437 if (IncreaseBuf(dest, 1, -1) < 0) {
1438 /* WHUT? no more mem? bail out. */
1445 if ( (current_token == 0 ) && /* are we in our target token? */
1446 (!IsEmptyStr(s) ) &&
1447 (separator != *s) ) /* don't copy the token itself */
1449 dest->buf[len] = *s; /* Copy the payload */
1450 ++len; /* remember the bigger size. */
1456 /* did we reach the end? */
1457 if ((s > EndBuffer)) {
1458 EndBuffer = StrBufNOTNULL;
1459 *pStart = EndBuffer;
1462 *pStart = s; /* remember the position for the next run */
1465 /* sanitize our extracted token */
1466 dest->buf[len] = '\0';
1467 dest->BufUsed = len;
1474 * a string tokenizer
1475 * Source StringBuffer to read from
1476 * pStart pointer to the end of the last token. Feed with NULL.
1477 * separator tokenizer character
1478 * nTokens number of tokens to fastforward over
1479 * @returns -1 if not found, else length of token.
1481 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens) {
1482 const char *s, *EndBuffer; //* source * /
1483 int len = 0; //* running total length of extracted string * /
1484 int current_token = 0; //* token currently being processed * /
1486 if ((Source == NULL) || (Source->BufUsed ==0)) {
1490 return Source->BufUsed;
1492 if (*pStart == NULL)
1493 *pStart = Source->buf;
1495 EndBuffer = Source->buf + Source->BufUsed;
1497 if ((*pStart < Source->buf) ||
1498 (*pStart > EndBuffer)) {
1504 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1505 if (*s == separator) {
1508 if (current_token >= nTokens) {
1520 // a string tokenizer to fetch an integer
1521 // Source StringBuffer to read from
1522 // pStart Cursor on the tokenstring
1523 // separator tokenizer character
1524 // returns 0 if not found, else integer representation of the token
1525 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator) {
1534 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1541 // a string tokenizer to fetch a long integer
1542 // Source StringBuffer to read from
1543 // pStart Cursor on the tokenstring
1544 // separator tokenizer character
1545 // returns 0 if not found, else long integer representation of the token
1546 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator) {
1555 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1562 // a string tokenizer to fetch an unsigned long
1563 // Source StringBuffer to read from
1564 // pStart Cursor on the tokenstring
1565 // separator tokenizer character
1566 // returns 0 if not found, else unsigned long representation of the token
1567 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator) {
1577 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1581 return (unsigned long) atol(pnum);
1588 /*******************************************************************************
1589 * Escape Appending *
1590 *******************************************************************************/
1592 // Escape a string for feeding out as a URL while appending it to a Buffer
1593 // OutBuf the output buffer
1594 // In Buffer to encode
1595 // PlainIn way in from plain old c strings
1596 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1597 const char *pch, *pche;
1601 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) ) {
1604 if (PlainIn != NULL) {
1605 len = strlen(PlainIn);
1611 pche = pch + In->BufUsed;
1618 pt = OutBuf->buf + OutBuf->BufUsed;
1619 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1621 while (pch < pche) {
1623 IncreaseBuf(OutBuf, 1, -1);
1624 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1625 pt = OutBuf->buf + OutBuf->BufUsed;
1628 if((*pch >= 'a' && *pch <= 'z') ||
1629 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1630 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1631 (*pch == '!') || (*pch == '_') ||
1632 (*pch == ',') || (*pch == '.'))
1639 *(pt + 1) = HexList[(unsigned char)*pch][0];
1640 *(pt + 2) = HexList[(unsigned char)*pch][1];
1642 OutBuf->BufUsed += 3;
1650 // Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1651 // OutBuf the output buffer
1652 // In Buffer to encode
1653 // PlainIn way in from plain old c strings
1654 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1655 const char *pch, *pche;
1659 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1661 if (PlainIn != NULL) {
1662 len = strlen(PlainIn);
1668 pche = pch + In->BufUsed;
1675 pt = OutBuf->buf + OutBuf->BufUsed;
1676 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1678 while (pch < pche) {
1680 IncreaseBuf(OutBuf, 1, -1);
1681 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1682 pt = OutBuf->buf + OutBuf->BufUsed;
1685 if((*pch >= 'a' && *pch <= 'z') ||
1686 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1687 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1688 (*pch == '!') || (*pch == '_') ||
1689 (*pch == ',') || (*pch == '.'))
1696 *(pt + 1) = HexList[(unsigned char)*pch][0];
1697 *(pt + 2) = HexList[(unsigned char)*pch][1];
1699 OutBuf->BufUsed += 3;
1707 // append a string with characters having a special meaning in xml encoded to the buffer
1708 // OutBuf the output buffer
1709 // In Buffer to encode
1710 // PlainIn way in from plain old c strings
1711 // PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1712 // OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1713 void StrBufXMLEscAppend(StrBuf *OutBuf,
1715 const char *PlainIn,
1717 int OverrideLowChars)
1719 const char *pch, *pche;
1724 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1726 if (PlainIn != NULL) {
1728 len = strlen((const char*)PlainIn);
1735 pch = (const char*)In->buf;
1736 pche = pch + In->BufUsed;
1743 pt = OutBuf->buf + OutBuf->BufUsed;
1744 /**< we max append 6 chars at once plus the \0 */
1745 pte = OutBuf->buf + OutBuf->BufSize - 6;
1747 while (pch < pche) {
1749 OutBuf->BufUsed = pt - OutBuf->buf;
1750 IncreaseBuf(OutBuf, 1, -1);
1751 pte = OutBuf->buf + OutBuf->BufSize - 6;
1752 /**< we max append 3 chars at once plus the \0 */
1754 pt = OutBuf->buf + OutBuf->BufUsed;
1758 memcpy(pt, HKEY("<"));
1762 else if (*pch == '>') {
1763 memcpy(pt, HKEY(">"));
1767 else if (*pch == '&') {
1768 memcpy(pt, HKEY("&"));
1772 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
1777 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
1780 while ((IsUtf8Sequence > 0) &&
1793 *pt = HexList[*(unsigned char*)pch][0];
1795 *pt = HexList[*(unsigned char*)pch][1];
1804 OutBuf->BufUsed = pt - OutBuf->buf;
1809 * append a string in hex encoding to the buffer
1810 * OutBuf the output buffer
1811 * In Buffer to encode
1812 * PlainIn way in from plain old c strings
1813 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1815 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1817 const unsigned char *pch, *pche;
1821 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1823 if (PlainIn != NULL) {
1825 len = strlen((const char*)PlainIn);
1832 pch = (const unsigned char*)In->buf;
1833 pche = pch + In->BufUsed;
1840 pt = OutBuf->buf + OutBuf->BufUsed;
1841 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1843 while (pch < pche) {
1845 IncreaseBuf(OutBuf, 1, -1);
1846 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1847 pt = OutBuf->buf + OutBuf->BufUsed;
1850 *pt = HexList[*pch][0];
1852 *pt = HexList[*pch][1];
1853 pt ++; pch ++; OutBuf->BufUsed += 2;
1859 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks) {
1865 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1867 if (PlainIn != NULL) {
1869 len = strlen(PlainIn);
1882 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
1884 if (ExpectLen > OutBuf->BufSize)
1885 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
1888 pt = OutBuf->buf + OutBuf->BufUsed;
1890 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
1893 OutBuf->BufUsed += len;
1898 // append a string in hex encoding to the buffer
1899 // OutBuf the output buffer
1900 // In Buffer to encode
1901 // PlainIn way in from plain old c strings
1902 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1903 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1907 * Append a string, escaping characters which have meaning in HTML.
1909 * Target target buffer
1910 * Source source buffer; set to NULL if you just have a C-String
1911 * PlainIn Plain-C string to append; set to NULL if unused
1912 * nbsp If nonzero, spaces are converted to non-breaking spaces.
1913 * nolinebreaks if set to 1, linebreaks are removed from the string.
1914 * if set to 2, linebreaks are replaced by <br/>
1916 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1918 const char *aptr, *eiptr;
1922 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1925 if (PlainIn != NULL) {
1927 len = strlen(PlainIn);
1932 eiptr = aptr + Source->BufUsed;
1933 len = Source->BufUsed;
1939 bptr = Target->buf + Target->BufUsed;
1940 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1942 while (aptr < eiptr){
1944 IncreaseBuf(Target, 1, -1);
1945 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1946 bptr = Target->buf + Target->BufUsed;
1949 memcpy(bptr, "<", 4);
1951 Target->BufUsed += 4;
1953 else if (*aptr == '>') {
1954 memcpy(bptr, ">", 4);
1956 Target->BufUsed += 4;
1958 else if (*aptr == '&') {
1959 memcpy(bptr, "&", 5);
1961 Target->BufUsed += 5;
1963 else if (*aptr == '"') {
1964 memcpy(bptr, """, 6);
1966 Target->BufUsed += 6;
1968 else if (*aptr == '\'') {
1969 memcpy(bptr, "'", 5);
1971 Target->BufUsed += 5;
1973 else if (*aptr == LB) {
1978 else if (*aptr == RB) {
1983 else if (*aptr == QU) {
1988 else if ((*aptr == 32) && (nbsp == 1)) {
1989 memcpy(bptr, " ", 6);
1991 Target->BufUsed += 6;
1993 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1994 *bptr='\0'; /* nothing */
1996 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1997 memcpy(bptr, "<br/>", 11);
1999 Target->BufUsed += 11;
2003 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2004 *bptr='\0'; /* nothing */
2014 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2016 return Target->BufUsed;
2020 * Append a string, escaping characters which have meaning in HTML.
2021 * Converts linebreaks into blanks; escapes single quotes
2022 * Target target buffer
2023 * Source source buffer; set to NULL if you just have a C-String
2024 * PlainIn Plain-C string to append; set to NULL if unused
2026 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2028 const char *aptr, *eiptr;
2032 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2035 if (PlainIn != NULL) {
2037 len = strlen(PlainIn);
2042 eiptr = aptr + Source->BufUsed;
2043 len = Source->BufUsed;
2049 eptr = Target->buf + Target->BufSize - 8;
2050 tptr = Target->buf + Target->BufUsed;
2052 while (aptr < eiptr){
2054 IncreaseBuf(Target, 1, -1);
2055 eptr = Target->buf + Target->BufSize - 8;
2056 tptr = Target->buf + Target->BufUsed;
2059 if (*aptr == '\n') {
2063 else if (*aptr == '\r') {
2067 else if (*aptr == '\'') {
2073 Target->BufUsed += 5;
2086 * Append a string, escaping characters which have meaning in ICAL.
2088 * Target target buffer
2089 * Source source buffer; set to NULL if you just have a C-String
2090 * PlainIn Plain-C string to append; set to NULL if unused
2092 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2094 const char *aptr, *eiptr;
2098 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2101 if (PlainIn != NULL) {
2103 len = strlen(PlainIn);
2108 eiptr = aptr + Source->BufUsed;
2109 len = Source->BufUsed;
2115 eptr = Target->buf + Target->BufSize - 8;
2116 tptr = Target->buf + Target->BufUsed;
2118 while (aptr < eiptr){
2119 if(tptr + 3 >= eptr) {
2120 IncreaseBuf(Target, 1, -1);
2121 eptr = Target->buf + Target->BufSize - 8;
2122 tptr = Target->buf + Target->BufUsed;
2125 if (*aptr == '\n') {
2132 else if (*aptr == '\r') {
2139 else if (*aptr == ',') {
2155 * Append a string, escaping characters which have meaning in JavaScript strings .
2157 * Target target buffer
2158 * Source source buffer; set to NULL if you just have a C-String
2159 * PlainIn Plain-C string to append; set to NULL if unused
2160 * @returns size of result or -1
2162 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2164 const char *aptr, *eiptr;
2169 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2172 if (PlainIn != NULL) {
2174 len = strlen(PlainIn);
2179 eiptr = aptr + Source->BufUsed;
2180 len = Source->BufUsed;
2186 bptr = Target->buf + Target->BufUsed;
2187 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2189 while (aptr < eiptr){
2191 IncreaseBuf(Target, 1, -1);
2192 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2193 bptr = Target->buf + Target->BufUsed;
2197 memcpy(bptr, HKEY("\\n"));
2199 Target->BufUsed += 2;
2202 memcpy(bptr, HKEY("\\r"));
2204 Target->BufUsed += 2;
2211 Target->BufUsed += 2;
2214 if ((*(aptr + 1) == 'u') &&
2215 isxdigit(*(aptr + 2)) &&
2216 isxdigit(*(aptr + 3)) &&
2217 isxdigit(*(aptr + 4)) &&
2218 isxdigit(*(aptr + 5)))
2219 { /* oh, a unicode escaper. let it pass through. */
2220 memcpy(bptr, aptr, 6);
2223 Target->BufUsed += 6;
2231 Target->BufUsed += 2;
2239 Target->BufUsed += 2;
2246 Target->BufUsed += 2;
2253 Target->BufUsed += 2;
2256 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2257 while (IsUtf8Sequence > 0){
2260 if (--IsUtf8Sequence)
2268 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2270 return Target->BufUsed;
2273 // Append a string, escaping characters which have meaning in HTML + json.
2275 // Target target buffer
2276 // Source source buffer; set to NULL if you just have a C-String
2277 // PlainIn Plain-C string to append; set to NULL if unused
2278 // nbsp If nonzero, spaces are converted to non-breaking spaces.
2279 // nolinebreaks if set to 1, linebreaks are removed from the string.
2280 // if set to 2, linebreaks are replaced by <br/>
2281 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks) {
2282 const char *aptr, *eiptr;
2285 int IsUtf8Sequence = 0;
2287 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL)) {
2290 if (PlainIn != NULL) {
2292 len = strlen(PlainIn);
2297 eiptr = aptr + Source->BufUsed;
2298 len = Source->BufUsed;
2305 bptr = Target->buf + Target->BufUsed;
2306 eptr = Target->buf + Target->BufSize - 11; // our biggest unit to put in...
2308 while (aptr < eiptr) {
2310 IncreaseBuf(Target, 1, -1);
2311 eptr = Target->buf + Target->BufSize - 11; // our biggest unit to put in...
2312 bptr = Target->buf + Target->BufUsed;
2316 memcpy(bptr, HKEY("<"));
2318 Target->BufUsed += 4;
2321 memcpy(bptr, HKEY(">"));
2323 Target->BufUsed += 4;
2326 memcpy(bptr, HKEY("&"));
2328 Target->BufUsed += 5;
2341 switch (nolinebreaks) {
2343 *bptr='\0'; /* nothing */
2346 memcpy(bptr, HKEY("<br/>"));
2348 Target->BufUsed += 11;
2351 memcpy(bptr, HKEY("\\n"));
2353 Target->BufUsed += 2;
2357 switch (nolinebreaks) {
2360 *bptr='\0'; /* nothing */
2363 memcpy(bptr, HKEY("\\r"));
2365 Target->BufUsed += 2;
2375 Target->BufUsed += 2;
2378 if ((*(aptr + 1) == 'u') &&
2379 isxdigit(*(aptr + 2)) &&
2380 isxdigit(*(aptr + 3)) &&
2381 isxdigit(*(aptr + 4)) &&
2382 isxdigit(*(aptr + 5)))
2383 { /* oh, a unicode escaper. let it pass through. */
2384 memcpy(bptr, aptr, 6);
2387 Target->BufUsed += 6;
2394 Target->BufUsed += 2;
2402 Target->BufUsed += 2;
2409 Target->BufUsed += 2;
2416 Target->BufUsed += 2;
2420 memcpy(bptr, HKEY(" "));
2422 Target->BufUsed += 6;
2426 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2427 while (IsUtf8Sequence > 0){
2430 if (--IsUtf8Sequence)
2438 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2440 return Target->BufUsed;
2445 * replace all non-Ascii characters by another
2446 * Buf buffer to inspect
2447 * repl charater to stamp over non ascii chars
2449 void StrBufAsciify(StrBuf *Buf, const char repl) {
2452 for (offset = 0; offset < Buf->BufUsed; offset ++)
2453 if (!isascii(Buf->buf[offset]))
2454 Buf->buf[offset] = repl;
2459 // unhide special chars hidden to the HTML escaper
2460 // target buffer to put the unescaped string in
2461 // source buffer to unescape
2462 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) {
2466 if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2471 FlushStrBuf(target);
2473 len = source->BufUsed;
2474 for (a = 0; a < len; ++a) {
2475 if (target->BufUsed >= target->BufSize)
2476 IncreaseBuf(target, 1, -1);
2478 if (source->buf[a] == '=') {
2479 hex[0] = source->buf[a + 1];
2480 hex[1] = source->buf[a + 2];
2483 sscanf(hex, "%02x", &b);
2484 target->buf[target->BufUsed] = b;
2485 target->buf[++target->BufUsed] = 0;
2489 target->buf[target->BufUsed] = source->buf[a];
2490 target->buf[++target->BufUsed] = 0;
2497 * hide special chars from the HTML escapers and friends
2498 * target buffer to put the escaped string in
2499 * source buffer to escape
2501 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) {
2505 FlushStrBuf(target);
2507 if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2511 len = source->BufUsed;
2512 for (i=0; i<len; ++i) {
2513 if (target->BufUsed + 4 >= target->BufSize)
2514 IncreaseBuf(target, 1, -1);
2515 if ( (isalnum(source->buf[i])) ||
2516 (source->buf[i]=='-') ||
2517 (source->buf[i]=='_') ) {
2518 target->buf[target->BufUsed++] = source->buf[i];
2521 sprintf(&target->buf[target->BufUsed],
2523 (0xFF &source->buf[i]));
2524 target->BufUsed += 3;
2527 target->buf[target->BufUsed + 1] = '\0';
2531 /*******************************************************************************
2532 * Quoted Printable de/encoding *
2533 *******************************************************************************/
2536 * decode a buffer from base 64 encoding; destroys original
2537 * Buf Buffor to transform
2539 int StrBufDecodeBase64(StrBuf *Buf) {
2547 xferbuf = (char*) malloc(Buf->BufSize);
2548 if (xferbuf == NULL)
2552 siz = CtdlDecodeBase64(xferbuf, Buf->buf, Buf->BufUsed);
2557 Buf->buf[Buf->BufUsed] = '\0';
2563 * decode a buffer from base 64 encoding; expects targetbuffer
2564 * BufIn Buffor to transform
2565 * BufOut Buffer to put result into
2567 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut) {
2568 if ((BufIn == NULL) || (BufOut == NULL))
2571 if (BufOut->BufSize < BufIn->BufUsed) {
2572 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2575 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf, BufIn->buf, BufIn->BufUsed);
2576 return BufOut->BufUsed;
2579 typedef struct __z_enc_stream {
2585 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err) {
2592 z_enc_stream *stream;
2595 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2596 memset(stream, 0, sizeof(z_enc_stream));
2597 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2598 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2600 err = inflateInit(&stream->zstream);
2603 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2607 return (vStreamT*) stream;
2612 z_enc_stream *stream;
2615 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2616 memset(stream, 0, sizeof(z_enc_stream));
2617 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2618 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2619 /* write gzip header */
2620 stream->OutBuf.BufUsed = snprintf
2621 (stream->OutBuf.buf,
2622 stream->OutBuf.BufSize,
2623 "%c%c%c%c%c%c%c%c%c%c",
2624 gz_magic[0], gz_magic[1], Z_DEFLATED,
2625 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2628 err = deflateInit2(&stream->zstream,
2629 ZLibCompressionRatio,
2633 Z_DEFAULT_STRATEGY);
2635 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2639 return (vStreamT*) stream;
2650 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err) {
2655 if ((vStream == NULL) || (*vStream==NULL)) {
2656 *Err = strerror(EINVAL);
2661 //case eBase64Encode:
2662 //case eBase64Decode:
2668 z_enc_stream *stream = (z_enc_stream *)*vStream;
2669 (void)inflateEnd(&stream->zstream);
2670 free(stream->OutBuf.buf);
2675 z_enc_stream *stream = (z_enc_stream *)*vStream;
2676 err = deflateEnd(&stream->zstream);
2681 free(stream->OutBuf.buf);
2692 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err) {
2696 //case eBase64Encode:
2701 //case eBase64Decode:
2708 z_enc_stream *stream = (z_enc_stream *)vStream;
2709 int org_outbuf_len = stream->OutBuf.BufUsed;
2711 unsigned int chunkavail;
2713 if (In->ReadWritePointer != NULL)
2715 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2716 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
2717 (In->ReadWritePointer - In->Buf->buf);
2721 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2722 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2725 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2726 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2728 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
2730 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
2732 if (Target && (LastChunk || (stream->OutBuf.BufUsed != org_outbuf_len))) {
2733 iSwapBuffers(Target->Buf, &stream->OutBuf);
2736 if (stream->zstream.avail_in == 0) {
2737 FlushStrBuf(In->Buf);
2738 In->ReadWritePointer = NULL;
2741 if (stream->zstream.avail_in < 64) {
2742 memmove(In->Buf->buf,
2743 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2744 stream->zstream.avail_in);
2746 In->Buf->BufUsed = stream->zstream.avail_in;
2747 In->Buf->buf[In->Buf->BufUsed] = '\0';
2750 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2753 rc = (LastChunk && (err != Z_FINISH));
2754 if (!rc && (err != Z_OK)) {
2761 z_enc_stream *stream = (z_enc_stream *)vStream;
2762 int org_outbuf_len = stream->zstream.total_out;
2765 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
2766 if (In->ReadWritePointer != NULL) {
2767 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2768 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - (In->ReadWritePointer - In->Buf->buf);
2771 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2772 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2776 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2777 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2779 err = inflate(&stream->zstream, Z_NO_FLUSH);
2781 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
2784 err = Z_DATA_ERROR; /* and fall through */
2789 (void)inflateEnd(&stream->zstream);
2793 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
2795 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
2797 if (stream->zstream.avail_in == 0) {
2798 FlushStrBuf(In->Buf);
2799 In->ReadWritePointer = NULL;
2802 if (stream->zstream.avail_in < 64) {
2803 memmove(In->Buf->buf,
2804 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2805 stream->zstream.avail_in);
2807 In->Buf->BufUsed = stream->zstream.avail_in;
2808 In->Buf->buf[In->Buf->BufUsed] = '\0';
2812 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2826 * decode a buffer from base 64 encoding; destroys original
2827 * Buf Buffor to transform
2829 int StrBufDecodeHex(StrBuf *Buf) {
2831 char *pch, *pche, *pchi;
2833 if (Buf == NULL) return -1;
2835 pch = pchi = Buf->buf;
2836 pche = pch + Buf->BufUsed;
2838 while (pchi < pche){
2839 ch = decode_hex(pchi);
2846 Buf->BufUsed = pch - Buf->buf;
2847 return Buf->BufUsed;
2851 * replace all chars >0x20 && < 0x7F with Mute
2852 * Mute char to put over invalid chars
2853 * Buf Buffor to transform
2855 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2859 if (Buf == NULL) return -1;
2860 pch = (unsigned char *)Buf->buf;
2861 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2862 if ((*pch < 0x20) || (*pch > 0x7F))
2866 return Buf->BufUsed;
2871 * remove escaped strings from i.e. the url string (like %20 for blanks)
2872 * Buf Buffer to translate
2873 * StripBlanks Reduce several blanks to one?
2875 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2884 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2885 Buf->buf[Buf->BufUsed - 1] = '\0';
2890 while (a < Buf->BufUsed) {
2891 if (Buf->buf[a] == '+')
2893 else if (Buf->buf[a] == '%') {
2894 /* don't let % chars through, rather truncate the input. */
2895 if (a + 2 > Buf->BufUsed) {
2900 hex[0] = Buf->buf[a + 1];
2901 hex[1] = Buf->buf[a + 2];
2904 sscanf(hex, "%02x", &b);
2905 Buf->buf[a] = (char) b;
2906 len = Buf->BufUsed - a - 2;
2908 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2920 * RFC2047-encode a header field if necessary.
2921 * If no non-ASCII characters are found, the string
2922 * will be copied verbatim without encoding.
2924 * target Target buffer.
2925 * source Source string to be encoded.
2926 * @returns encoded length; -1 if non success.
2928 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2930 const char headerStr[] = "=?UTF-8?Q?";
2931 int need_to_encode = 0;
2935 if ((source == NULL) ||
2939 while ((i < source->BufUsed) &&
2940 (!IsEmptyStr (&source->buf[i])) &&
2941 (need_to_encode == 0)) {
2942 if (((unsigned char) source->buf[i] < 32) ||
2943 ((unsigned char) source->buf[i] > 126)) {
2949 if (!need_to_encode) {
2950 if (*target == NULL) {
2951 *target = NewStrBufPlain(source->buf, source->BufUsed);
2954 FlushStrBuf(*target);
2955 StrBufAppendBuf(*target, source, 0);
2958 return (*target)->BufUsed;
2962 if (*target == NULL)
2963 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2964 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2965 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2966 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2967 (*target)->BufUsed = sizeof(headerStr) - 1;
2968 for (i=0; (i < source->BufUsed); ++i) {
2969 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2970 IncreaseBuf(*target, 1, 0);
2971 ch = (unsigned char) source->buf[i];
2980 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2981 (*target)->BufUsed += 3;
2985 (*target)->buf[(*target)->BufUsed] = '_';
2987 (*target)->buf[(*target)->BufUsed] = ch;
2988 (*target)->BufUsed++;
2992 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2993 IncreaseBuf(*target, 1, 0);
2995 (*target)->buf[(*target)->BufUsed++] = '?';
2996 (*target)->buf[(*target)->BufUsed++] = '=';
2997 (*target)->buf[(*target)->BufUsed] = '\0';
2998 return (*target)->BufUsed;;
3001 // Quoted-Printable encode a message; make it < 80 columns width.
3002 // source Source string to be encoded.
3003 // returns buffer with encoded message.
3004 StrBuf *StrBufQuotedPrintableEncode(const StrBuf *EncodeMe) {
3007 const char *ptr, *eptr;
3011 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3013 OEptr = OutBuf->buf + OutBuf->BufSize;
3014 ptr = EncodeMe->buf;
3015 eptr = EncodeMe->buf + EncodeMe->BufUsed;
3018 while (ptr < eptr) {
3019 if (Optr + 4 >= OEptr) {
3021 Offset = Optr - OutBuf->buf;
3022 OutBuf->BufUsed = Optr - OutBuf->buf;
3023 IncreaseBuf(OutBuf, 1, 0);
3024 Optr = OutBuf->buf + Offset;
3025 OEptr = OutBuf->buf + OutBuf->BufSize;
3027 if (*ptr == '\r') { // ignore carriage returns
3030 else if (*ptr == '\n') { // hard line break
3031 memcpy(Optr, HKEY("=0A"));
3036 else if (( (*ptr >= 32) && (*ptr <= 60) ) || ( (*ptr >= 62) && (*ptr <= 126) )) {
3046 *Optr = HexList[ch][0];
3048 *Optr = HexList[ch][1];
3054 if (LinePos > 72) { // soft line break
3055 if (isspace(*(Optr - 1))) {
3060 *Optr = HexList[ch][0];
3062 *Optr = HexList[ch][1];
3074 OutBuf->BufUsed = Optr - OutBuf->buf;
3080 static void AddRecipient(StrBuf *Target, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3083 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3084 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3086 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3087 StrBufRFC2047encode(&EncBuf, UserName);
3088 StrBufAppendBuf(Target, EncBuf, 0);
3089 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3090 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3092 if (StrLength(EmailAddress) > 0){
3093 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3094 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3095 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3101 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3102 * \param Recp Source list of email recipients
3103 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3104 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3105 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3106 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3108 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3110 const char *pch, *pche;
3111 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3113 if ((Recp == NULL) || (StrLength(Recp) == 0))
3117 pche = pch + StrLength(Recp);
3119 if (!CheckEncode(pch, -1, pche))
3120 return NewStrBufDup(Recp);
3122 Target = NewStrBufPlain(NULL, StrLength(Recp));
3124 while ((pch != NULL) && (pch < pche))
3126 while (isspace(*pch)) pch++;
3127 UserEnd = EmailStart = EmailEnd = NULL;
3129 if ((*pch == '"') || (*pch == '\'')) {
3130 UserStart = pch + 1;
3132 UserEnd = strchr(UserStart, *pch);
3133 if (UserEnd == NULL)
3134 break; ///TODO: Userfeedback??
3135 EmailStart = UserEnd + 1;
3136 while (isspace(*EmailStart))
3138 if (UserEnd == UserStart) {
3139 UserStart = UserEnd = NULL;
3142 if (*EmailStart == '<') {
3144 EmailEnd = strchr(EmailStart, '>');
3145 if (EmailEnd == NULL)
3146 EmailEnd = strchr(EmailStart, ',');
3150 EmailEnd = strchr(EmailStart, ',');
3152 if (EmailEnd == NULL)
3159 EmailEnd = strchr(UserStart, ',');
3160 if (EmailEnd == NULL) {
3161 EmailEnd = strchr(pch, '>');
3163 if (EmailEnd != NULL) {
3173 while ((EmailEnd > UserStart) && !gt &&
3174 ((*EmailEnd == ',') ||
3175 (*EmailEnd == '>') ||
3176 (isspace(*EmailEnd))))
3178 if (*EmailEnd == '>')
3183 if (EmailEnd == UserStart)
3187 EmailStart = strchr(UserStart, '<');
3188 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3190 UserEnd = EmailStart;
3192 while ((UserEnd > UserStart) &&
3193 isspace (*(UserEnd - 1)))
3196 if (UserStart >= UserEnd)
3197 UserStart = UserEnd = NULL;
3199 else { /* this is a local recipient... no domain, just a realname */
3200 EmailStart = UserStart;
3201 At = strchr(EmailStart, '@');
3207 EmailStart = UserStart;
3213 if ((UserStart != NULL) && (UserEnd != NULL))
3214 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3215 else if ((UserStart != NULL) && (UserEnd == NULL))
3216 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3218 FlushStrBuf(UserName);
3220 if ((EmailStart != NULL) && (EmailEnd != NULL))
3221 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3222 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3223 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3225 FlushStrBuf(EmailAddress);
3227 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3232 if ((pch != NULL) && (*pch == ','))
3234 if (pch != NULL) while (isspace(*pch))
3242 * replaces all occurances of 'search' by 'replace'
3243 * buf Buffer to modify
3244 * search character to search
3245 * replace character to replace search by
3247 void StrBufReplaceChars(StrBuf *buf, char search, char replace) {
3251 for (i=0; i<buf->BufUsed; i++)
3252 if (buf->buf[i] == search)
3253 buf->buf[i] = replace;
3258 * removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3259 * buf Buffer to modify
3261 void StrBufToUnixLF(StrBuf *buf)
3263 char *pche, *pchS, *pchT;
3267 pche = buf->buf + buf->BufUsed;
3268 pchS = pchT = buf->buf;
3274 if (*pchS != '\n') {
3283 buf->BufUsed = pchT - buf->buf;
3287 /*******************************************************************************
3288 * Iconv Wrapper; RFC822 de/encoding *
3289 *******************************************************************************/
3292 * Wrapper around iconv_open()
3293 * Our version adds aliases for non-standard Microsoft charsets
3294 * such as 'MS950', aliasing them to names like 'CP950'
3296 * tocode Target encoding
3297 * fromcode Source encoding
3298 * pic anonimized pointer to iconv struct
3300 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3303 iconv_t ic = (iconv_t)(-1) ;
3304 ic = iconv_open(tocode, fromcode);
3305 if (ic == (iconv_t)(-1) ) {
3306 char alias_fromcode[64];
3307 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3308 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3309 alias_fromcode[0] = 'C';
3310 alias_fromcode[1] = 'P';
3311 ic = iconv_open(tocode, alias_fromcode);
3314 *(iconv_t *)pic = ic;
3320 * find one chunk of a RFC822 encoded string
3321 * Buffer where to search
3322 * bptr where to start searching
3323 * @returns found position, NULL if none.
3325 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3328 /* Find the next ?Q? */
3329 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3332 end = strchr(bptr + 2, '?');
3337 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3338 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3339 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3340 (*(end + 2) == '?')) {
3341 /* skip on to the end of the cluster, the next ?= */
3342 end = strstr(end + 3, "?=");
3345 /* sort of half valid encoding, try to find an end. */
3346 end = strstr(bptr, "?=");
3353 * convert one buffer according to the preselected iconv pointer PIC
3354 * ConvertBuf buffer we need to translate
3355 * TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3356 * pic Pointer to the iconv-session Object
3358 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3364 char *ibuf; /**< Buffer of characters to be converted */
3365 char *obuf; /**< Buffer for converted characters */
3366 size_t ibuflen; /**< Length of input buffer */
3367 size_t obuflen; /**< Length of output buffer */
3370 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3373 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3374 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3375 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3377 ic = *(iconv_t*)pic;
3378 ibuf = ConvertBuf->buf;
3379 ibuflen = ConvertBuf->BufUsed;
3381 obuflen = TmpBuf->BufSize;
3383 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3386 if (errno == E2BIG) {
3388 IncreaseBuf(TmpBuf, 0, 0);
3393 else if (errno == EILSEQ){
3394 /* hm, invalid utf8 sequence... what to do now? */
3395 /* An invalid multibyte sequence has been encountered in the input */
3397 else if (errno == EINVAL) {
3398 /* An incomplete multibyte sequence has been encountered in the input. */
3401 FlushStrBuf(TmpBuf);
3404 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3405 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3407 /* little card game: wheres the red lady? */
3408 iSwapBuffers(ConvertBuf, TmpBuf);
3409 FlushStrBuf(TmpBuf);
3416 * catches one RFC822 encoded segment, and decodes it.
3417 * Target buffer to fill with result
3418 * DecodeMe buffer with stuff to process
3419 * SegmentStart points to our current segment in DecodeMe
3420 * SegmentEnd Points to the end of our current segment in DecodeMe
3421 * ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3422 * ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3423 * FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3425 inline static void DecodeSegment(StrBuf *Target,
3426 const StrBuf *DecodeMe,
3427 const char *SegmentStart,
3428 const char *SegmentEnd,
3430 StrBuf *ConvertBuf2,
3431 StrBuf *FoundCharset)
3437 iconv_t ic = (iconv_t)(-1);
3441 /* Now we handle foreign character sets properly encoded
3442 * in RFC2047 format.
3444 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3445 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3446 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3447 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3448 if (FoundCharset != NULL) {
3449 FlushStrBuf(FoundCharset);
3450 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3452 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3453 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3455 *encoding = toupper(*encoding);
3456 if (*encoding == 'B') { /**< base64 */
3457 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3458 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3459 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3461 ConvertBuf->BufUsed);
3463 else if (*encoding == 'Q') { /**< quoted-printable */
3467 while (pos < ConvertBuf->BufUsed)
3469 if (ConvertBuf->buf[pos] == '_')
3470 ConvertBuf->buf[pos] = ' ';
3474 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3475 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3477 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3480 ConvertBuf->BufUsed);
3483 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3486 ctdl_iconv_open("UTF-8", charset, &ic);
3487 if (ic != (iconv_t)(-1) ) {
3489 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3490 StrBufAppendBuf(Target, ConvertBuf2, 0);
3495 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3501 * Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3502 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3503 * Target where to put the decoded string to
3504 * DecodeMe buffer with encoded string
3505 * DefaultCharset if we don't find one, which should we use?
3506 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3507 * put it here for later use where no string might be known.
3509 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3512 StrBuf *ConvertBuf2;
3513 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3514 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3516 StrBuf_RFC822_2_Utf8(Target,
3522 FreeStrBuf(&ConvertBuf);
3523 FreeStrBuf(&ConvertBuf2);
3527 * Handle subjects with RFC2047 encoding such as:
3528 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3529 * Target where to put the decoded string to
3530 * DecodeMe buffer with encoded string
3531 * DefaultCharset if we don't find one, which should we use?
3532 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3533 * put it here for later use where no string might be known.
3534 * ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3535 * ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3537 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3538 const StrBuf *DecodeMe,
3539 const StrBuf* DefaultCharset,
3540 StrBuf *FoundCharset,
3542 StrBuf *ConvertBuf2)
3544 StrBuf *DecodedInvalidBuf = NULL;
3545 const StrBuf *DecodeMee = DecodeMe;
3546 const char *start, *end, *next, *nextend, *ptr = NULL;
3548 iconv_t ic = (iconv_t)(-1) ;
3553 int illegal_non_rfc2047_encoding = 0;
3556 if (DecodeMe == NULL)
3558 /* Sometimes, badly formed messages contain strings which were simply
3559 * written out directly in some foreign character set instead of
3560 * using RFC2047 encoding. This is illegal but we will attempt to
3561 * handle it anyway by converting from a user-specified default
3562 * charset to UTF-8 if we see any nonprintable characters.
3565 for (i=0; i<DecodeMe->BufUsed; ++i) {
3566 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3567 illegal_non_rfc2047_encoding = 1;
3572 if ((illegal_non_rfc2047_encoding) &&
3573 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3574 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3577 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3578 if (ic != (iconv_t)(-1) ) {
3579 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3580 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3581 DecodeMee = DecodedInvalidBuf;
3587 /* pre evaluate the first pair */
3589 start = strstr(DecodeMee->buf, "=?");
3590 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3592 end = FindNextEnd (DecodeMee, start + 2);
3594 StrBufAppendBuf(Target, DecodeMee, 0);
3595 FreeStrBuf(&DecodedInvalidBuf);
3600 if (start != DecodeMee->buf) {
3603 nFront = start - DecodeMee->buf;
3604 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3607 * Since spammers will go to all sorts of absurd lengths to get their
3608 * messages through, there are LOTS of corrupt headers out there.
3609 * So, prevent a really badly formed RFC2047 header from throwing
3610 * this function into an infinite loop.
3612 while ((start != NULL) &&
3619 DecodeSegment(Target,
3627 next = strstr(end, "=?");
3629 if ((next != NULL) &&
3631 nextend = FindNextEnd(DecodeMee, next);
3632 if (nextend == NULL)
3635 /* did we find two partitions */
3636 if ((next != NULL) &&
3640 while ((ptr < next) &&
3647 * did we find a gab just filled with blanks?
3648 * if not, copy its stuff over.
3652 StrBufAppendBufPlain(Target,
3658 /* our next-pair is our new first pair now. */
3664 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3665 if ((end != NULL) && (end < nextend)) {
3667 while ( (ptr < nextend) &&
3674 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3676 FreeStrBuf(&DecodedInvalidBuf);
3679 /*******************************************************************************
3680 * Manipulating UTF-8 Strings *
3681 *******************************************************************************/
3684 * evaluate the length of an utf8 special character sequence
3685 * Char the character to examine
3686 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3688 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3691 unsigned char test = (1<<7);
3693 if ((*CharS & 0xC0) != 0xC0)
3697 ((test & ((unsigned char)*CharS)) != 0))
3702 if ((n > 6) || ((CharE - CharS) < n))
3708 * detect whether this char starts an utf-8 encoded char
3709 * Char character to inspect
3710 * @returns yes or no
3712 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3714 /** 11??.???? indicates an UTF8 Sequence. */
3715 return ((Char & 0xC0) == 0xC0);
3719 * measure the number of glyphs in an UTF8 string...
3720 * Buf string to measure
3721 * @returns the number of glyphs in Buf
3723 long StrBuf_Utf8StrLen(StrBuf *Buf)
3729 if ((Buf == NULL) || (Buf->BufUsed == 0))
3732 eptr = Buf->buf + Buf->BufUsed;
3733 while ((aptr < eptr) && (*aptr != '\0')) {
3734 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3735 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3736 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3748 * cuts a string after maxlen glyphs
3749 * Buf string to cut to maxlen glyphs
3750 * maxlen how long may the string become?
3751 * @returns current length of the string
3753 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3759 eptr = Buf->buf + Buf->BufUsed;
3760 while ((aptr < eptr) && (*aptr != '\0')) {
3761 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3762 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3763 while ((*aptr++ != '\0') && (m-- > 0));
3772 Buf->BufUsed = aptr - Buf->buf;
3773 return Buf->BufUsed;
3776 return Buf->BufUsed;
3781 /*******************************************************************************
3783 *******************************************************************************/
3786 * uses the same calling syntax as compress2(), but it
3787 * creates a stream compatible with HTTP "Content-encoding: gzip"
3788 * dest compressed buffer
3789 * destLen length of the compresed data
3790 * source source to encode
3791 * sourceLen length of source to encode
3792 * level compression level
3795 int ZEXPORT compress_gzip(Bytef * dest,
3797 const Bytef * source,
3801 /* write gzip header */
3802 snprintf((char *) dest, *destLen,
3803 "%c%c%c%c%c%c%c%c%c%c",
3804 gz_magic[0], gz_magic[1], Z_DEFLATED,
3805 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3808 /* normal deflate */
3811 stream.next_in = (Bytef *) source;
3812 stream.avail_in = (uInt) sourceLen;
3813 stream.next_out = dest + 10L; // after header
3814 stream.avail_out = (uInt) * destLen;
3815 if ((uLong) stream.avail_out != *destLen)
3818 stream.zalloc = (alloc_func) 0;
3819 stream.zfree = (free_func) 0;
3820 stream.opaque = (voidpf) 0;
3822 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3823 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3827 err = deflate(&stream, Z_FINISH);
3828 if (err != Z_STREAM_END) {
3829 deflateEnd(&stream);
3830 return err == Z_OK ? Z_BUF_ERROR : err;
3832 *destLen = stream.total_out + 10L;
3834 /* write CRC and Length */
3835 uLong crc = crc32(0L, source, sourceLen);
3837 for (n = 0; n < 4; ++n, ++*destLen) {
3838 dest[*destLen] = (int) (crc & 0xff);
3841 uLong len = stream.total_in;
3842 for (n = 0; n < 4; ++n, ++*destLen) {
3843 dest[*destLen] = (int) (len & 0xff);
3846 err = deflateEnd(&stream);
3853 * compress the buffer with gzip
3854 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3855 * Buf buffer whose content is to be gzipped
3857 int CompressBuffer(StrBuf *Buf)
3860 char *compressed_data = NULL;
3861 size_t compressed_len, bufsize;
3864 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3865 compressed_data = malloc(compressed_len);
3867 if (compressed_data == NULL)
3869 /* Flush some space after the used payload so valgrind shuts up... */
3870 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3871 Buf->buf[Buf->BufUsed + i++] = '\0';
3872 if (compress_gzip((Bytef *) compressed_data,
3875 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3878 Buf->buf = compressed_data;
3879 Buf->BufUsed = compressed_len;
3880 Buf->BufSize = bufsize;
3881 /* Flush some space after the used payload so valgrind shuts up... */
3883 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3884 Buf->buf[Buf->BufUsed + i++] = '\0';
3887 free(compressed_data);
3889 #endif /* HAVE_ZLIB */
3894 /*******************************************************************************
3895 * File I/O; Callbacks to libevent *
3896 *******************************************************************************/
3898 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3903 if ((FB == NULL) || (FB->Buf == NULL))
3907 * check whether the read pointer is somewhere in a range
3908 * where a cut left is inexpensive
3911 if (FB->ReadWritePointer != NULL) {
3914 already_read = FB->ReadWritePointer - FB->Buf->buf;
3915 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3917 if (already_read != 0) {
3920 unread = FB->Buf->BufUsed - already_read;
3922 /* else nothing to compact... */
3924 FB->ReadWritePointer = FB->Buf->buf;
3925 bufremain = FB->Buf->BufSize;
3927 else if ((unread < 64) || (bufremain < already_read)) {
3929 * if its just a tiny bit remaining, or we run out of space...
3932 FB->Buf->BufUsed = unread;
3933 if (unread < already_read)
3934 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3936 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3937 FB->ReadWritePointer = FB->Buf->buf;
3938 bufremain = FB->Buf->BufSize - unread - 1;
3940 else if (bufremain < (FB->Buf->BufSize / 10)) {
3941 /* get a bigger buffer */
3943 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3945 FB->ReadWritePointer = FB->Buf->buf + unread;
3947 bufremain = FB->Buf->BufSize - unread - 1;
3948 /*TODO: special increase function that won't copy the already read! */
3951 else if (bufremain < 10) {
3952 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3954 FB->ReadWritePointer = FB->Buf->buf;
3956 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3961 FB->ReadWritePointer = FB->Buf->buf;
3962 bufremain = FB->Buf->BufSize - 1;
3965 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3968 FB->Buf->BufUsed += n;
3969 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3975 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB) {
3979 if ((FB == NULL) || (FB->Buf == NULL))
3982 if (FB->ReadWritePointer != NULL)
3984 WriteRemain = FB->Buf->BufUsed -
3985 (FB->ReadWritePointer -
3989 FB->ReadWritePointer = FB->Buf->buf;
3990 WriteRemain = FB->Buf->BufUsed;
3993 n = write(fd, FB->ReadWritePointer, WriteRemain);
3995 FB->ReadWritePointer += n;
3997 if (FB->ReadWritePointer ==
3998 FB->Buf->buf + FB->Buf->BufUsed)
4000 FlushStrBuf(FB->Buf);
4001 FB->ReadWritePointer = NULL;
4004 // check whether we've got something to write
4005 // get the maximum chunk plus the pointer we can send
4006 // write whats there
4007 // if not all was sent, remember the send pointer for the next time
4008 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4014 // extract a "next line" from Buf; Ptr to persist across several iterations
4015 // LineBuf your line will be copied here.
4016 // FB BLOB with lines of text...
4017 // Ptr moved arround to keep the next-line across several iterations
4018 // has to be &NULL on start; will be &NotNULL on end of buffer
4019 // returns size of copied buffer
4020 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB) {
4021 const char *aptr, *ptr, *eptr;
4024 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4028 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4029 FB->ReadWritePointer = StrBufNOTNULL;
4033 FlushStrBuf(LineBuf);
4034 if (FB->ReadWritePointer == NULL)
4035 ptr = aptr = FB->Buf->buf;
4037 ptr = aptr = FB->ReadWritePointer;
4039 optr = LineBuf->buf;
4040 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4041 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4043 while ((ptr <= eptr) && (*ptr != '\n') && (*ptr != '\r') ) {
4047 LineBuf->BufUsed = optr - LineBuf->buf;
4048 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4049 optr = LineBuf->buf + LineBuf->BufUsed;
4050 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4055 if (optr > LineBuf->buf)
4057 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4058 LineBuf->BufUsed = optr - LineBuf->buf;
4060 if ((FB->ReadWritePointer != NULL) && (FB->ReadWritePointer != FB->Buf->buf)) {
4061 // Ok, the client application read all the data
4062 // it was interested in so far. Since there is more to read,
4063 // we now shrink the buffer, and move the rest over.
4064 StrBufCutLeft(FB->Buf, FB->ReadWritePointer - FB->Buf->buf);
4065 FB->ReadWritePointer = FB->Buf->buf;
4067 return eMustReadMore;
4070 LineBuf->BufUsed = optr - LineBuf->buf;
4072 if ((ptr <= eptr) && (*ptr == '\r'))
4074 if ((ptr <= eptr) && (*ptr == '\n'))
4078 FB->ReadWritePointer = ptr;
4081 FlushStrBuf(FB->Buf);
4082 FB->ReadWritePointer = NULL;
4085 return eReadSuccess;
4089 * check whether the chunk-buffer has more data waiting or not.
4090 * FB Chunk-Buffer to inspect
4092 eReadState StrBufCheckBuffer(IOBuffer *FB)
4096 if (FB->Buf->BufUsed == 0)
4097 return eReadSuccess;
4098 if (FB->ReadWritePointer == NULL)
4099 return eBufferNotEmpty;
4100 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4101 return eBufferNotEmpty;
4102 return eReadSuccess;
4106 long IOBufferStrLength(IOBuffer *FB) {
4107 if ((FB == NULL) || (FB->Buf == NULL))
4109 if (FB->ReadWritePointer == NULL)
4110 return StrLength(FB->Buf);
4112 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4116 /*******************************************************************************
4117 * File I/O; Prefer buffered read since its faster! *
4118 *******************************************************************************/
4121 * Read a line from socket
4122 * flushes and closes the FD on error
4123 * buf the buffer to get the input to
4124 * fd pointer to the filedescriptor to read
4125 * append Append to an existing string or replace?
4126 * Error strerror() on error
4127 * @returns numbers of chars read
4129 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4131 int len, rlen, slen;
4133 if ((buf == NULL) || (buf->buf == NULL)) {
4134 *Error = strerror(EINVAL);
4141 slen = len = buf->BufUsed;
4143 rlen = read(*fd, &buf->buf[len], 1);
4145 *Error = strerror(errno);
4152 if (buf->buf[len] == '\n')
4154 if (buf->buf[len] != '\r')
4156 if (len + 2 >= buf->BufSize) {
4158 buf->buf[len+1] = '\0';
4159 IncreaseBuf(buf, 1, -1);
4163 buf->buf[len] = '\0';
4168 // Read a line from socket
4169 // flushes and closes the FD on error
4170 // Line the line to read from the fd / I/O Buffer
4171 // buf the buffer to get the input to
4172 // fd pointer to the filedescriptor to read
4173 // timeout number of successless selects until we bail out
4174 // selectresolution how long to wait on each select
4175 // Error strerror() on error
4176 // returns numbers of chars read
4177 int StrBufTCP_read_buffered_line(StrBuf *Line,
4181 int selectresolution,
4185 int nSuccessLess = 0;
4192 if (buf->BufUsed > 0) {
4193 pch = strchr(buf->buf, '\n');
4196 len = pch - buf->buf;
4197 if (len > 0 && (*(pch - 1) == '\r') ) {
4200 StrBufSub(Line, buf, 0, len - rlen);
4201 StrBufCutLeft(buf, len + 1);
4206 if (buf->BufSize - buf->BufUsed < 10) {
4207 IncreaseBuf(buf, 1, -1);
4210 fdflags = fcntl(*fd, F_GETFL);
4211 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4213 while ((nSuccessLess < timeout) && (pch == NULL)) {
4215 tv.tv_sec = selectresolution;
4220 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4221 *Error = strerror(errno);
4227 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4231 rlen = read(*fd, &buf->buf[buf->BufUsed], buf->BufSize - buf->BufUsed - 1);
4233 *Error = strerror(errno);
4238 else if (rlen > 0) {
4240 buf->BufUsed += rlen;
4241 buf->buf[buf->BufUsed] = '\0';
4242 pch = strchr(buf->buf, '\n');
4243 if ((pch == NULL) && (buf->BufUsed + 10 > buf->BufSize) && (IncreaseBuf(buf, 1, -1) == -1)) {
4252 len = pch - buf->buf;
4253 if (len > 0 && (*(pch - 1) == '\r') )
4255 StrBufSub(Line, buf, 0, len - rlen);
4256 StrBufCutLeft(buf, len + 1);
4263 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4264 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4265 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4268 // Read a line from socket
4269 // flushes and closes the FD on error
4270 // Line where to append our Line read from the fd / I/O Buffer;
4271 // IOBuf the buffer to get the input to; lifetime pair to FD
4272 // Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4273 // fd pointer to the filedescriptor to read
4274 // timeout number of successless selects until we bail out
4275 // selectresolution how long to wait on each select
4276 // Error strerror() on error
4277 // returns numbers of chars read or -1 in case of error. "\n" will become 0
4278 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4283 int selectresolution,
4286 const char *pche = NULL;
4287 const char *pos = NULL;
4289 int len, rlen, retlen;
4290 int nSuccessLess = 0;
4292 const char *pch = NULL;
4298 if ((Line == NULL) || (Pos == NULL) || (IOBuf == NULL) || (*fd == -1)) {
4302 *Error = ErrRBLF_PreConditionFailed;
4307 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed)) {
4310 pche = IOBuf->buf + IOBuf->BufUsed;
4314 while ((pch < pche) && (*pch != '\n')) {
4315 if (Line->BufUsed + 10 > Line->BufSize) {
4317 apos = pcht - Line->buf;
4319 IncreaseBuf(Line, 1, -1);
4320 pcht = Line->buf + apos;
4328 if (len > 0 && (*(pch - 1) == '\r') ) {
4336 if ((pch >= pche) || (*pch == '\0')) {
4343 if ((pch != NULL) && (pch <= pche)) {
4344 if (pch + 1 >= pche) {
4357 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4359 if (IOBuf->BufSize - IOBuf->BufUsed < 10) {
4360 IncreaseBuf(IOBuf, 1, -1);
4363 fdflags = fcntl(*fd, F_GETFL);
4364 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4367 while ((nSuccessLess < timeout) &&
4376 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4377 *Error = strerror(errno);
4381 *Error = ErrRBLF_SelectFailed;
4384 if (! FD_ISSET(*fd, &rfds) != 0) {
4389 rlen = read(*fd, &IOBuf->buf[IOBuf->BufUsed], IOBuf->BufSize - IOBuf->BufUsed - 1);
4391 *Error = strerror(errno);
4396 else if (rlen > 0) {
4398 pLF = IOBuf->buf + IOBuf->BufUsed;
4399 IOBuf->BufUsed += rlen;
4400 IOBuf->buf[IOBuf->BufUsed] = '\0';
4402 pche = IOBuf->buf + IOBuf->BufUsed;
4404 while ((pLF < pche) && (*pLF != '\n'))
4406 if ((pLF >= pche) || (*pLF == '\0'))
4409 if (IOBuf->BufUsed + 10 > IOBuf->BufSize) {
4412 if (pLF != NULL) apos = pLF - IOBuf->buf;
4413 IncreaseBuf(IOBuf, 1, -1);
4414 if (pLF != NULL) pLF = IOBuf->buf + apos;
4427 if (len > 0 && (*(pLF - 1) == '\r') ) {
4430 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4431 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed) {
4436 return retlen + len;
4438 *Error = ErrRBLF_NotEnoughSentFromServer;
4443 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4445 * Input binary data from socket
4446 * flushes and closes the FD on error
4447 * Buf the buffer to get the input to
4448 * fd pointer to the filedescriptor to read
4449 * append Append to an existing string or replace?
4450 * nBytes the maximal number of bytes to read
4451 * Error strerror() on error
4452 * @returns numbers of chars read
4454 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4465 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1)) {
4466 *Error = ErrRBLF_BLOBPreConditionFailed;
4472 if (Buf->BufUsed + nBytes >= Buf->BufSize) {
4473 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4475 ptr = Buf->buf + Buf->BufUsed;
4477 fdflags = fcntl(*fd, F_GETFL);
4478 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4480 while ((nRead < nBytes) && (*fd != -1)) {
4487 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4488 *Error = strerror(errno);
4491 if (*Error == NULL) {
4492 *Error = ErrRBLF_SelectFailed;
4496 if (! FD_ISSET(*fd, &rfds) != 0) {
4502 if ((rlen = read(*fd,
4504 nBytes - nRead)) == -1) {
4507 *Error = strerror(errno);
4512 Buf->BufUsed += rlen;
4514 Buf->buf[Buf->BufUsed] = '\0';
4518 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4519 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4522 // Input binary data from socket
4523 // flushes and closes the FD on error
4524 // Blob put binary thing here
4525 // IOBuf the buffer to get the input to
4526 // Pos offset inside of IOBuf
4527 // fd pointer to the filedescriptor to read
4528 // append Append to an existing string or replace?
4529 // nBytes the maximal number of bytes to read
4530 // check whether we should search for '000\n' terminators in case of timeouts
4531 // Error strerror() on error
4532 // returns numbers of chars read
4533 int StrBufReadBLOBBuffered(StrBuf *Blob,
4546 int nAlreadyRead = 0;
4551 int nSuccessLess = 0;
4554 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL)) {
4557 *Error = ErrRBB_BLOBFPreConditionFailed;
4563 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4564 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4569 rlen = pos - IOBuf->buf;
4571 rlen = IOBuf->BufUsed - rlen;
4574 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed)) {
4575 if (rlen < nBytes) {
4576 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4577 Blob->BufUsed += rlen;
4578 Blob->buf[Blob->BufUsed] = '\0';
4579 nAlreadyRead = nRead = rlen;
4582 if (rlen >= nBytes) {
4583 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4584 Blob->BufUsed += nBytes;
4585 Blob->buf[Blob->BufUsed] = '\0';
4586 if (rlen == nBytes) {
4598 if (IOBuf->BufSize < nBytes - nRead) {
4599 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4603 fdflags = fcntl(*fd, F_GETFL);
4604 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4612 while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
4619 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4620 *Error = strerror(errno);
4623 if (*Error == NULL) {
4624 *Error = ErrRBLF_SelectFailed;
4628 if (! FD_ISSET(*fd, &rfds) != 0) {
4633 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
4634 if (rlen < 1) { // We will always get at least 1 byte unless the connection is broken
4637 *Error = strerror(errno);
4640 else if (rlen == 0){
4641 if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
4642 StrBufPlain(Blob, HKEY("\n000\n"));
4643 StrBufCutRight(Blob, 5);
4644 return Blob->BufUsed;
4646 else if (!IsNonBlock)
4648 else if (nSuccessLess > MaxTries) {
4650 *Error = ErrRBB_too_many_selects;
4654 else if (rlen > 0) {
4658 IOBuf->BufUsed += rlen;
4661 if (nSuccessLess >= MaxTries) {
4663 *Error = ErrRBB_too_many_selects;
4667 if (nRead > nBytes) {
4668 *Pos = IOBuf->buf + nBytes;
4670 Blob->buf[Blob->BufUsed] = '\0';
4671 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4675 return nRead + nAlreadyRead;
4679 // extract a "next line" from Buf; Ptr to persist across several iterations
4680 // LineBuf your line will be copied here.
4681 // Buf BLOB with lines of text...
4682 // Ptr moved arround to keep the next-line across several iterations
4683 // has to be &NULL on start; will be &NotNULL on end of buffer
4684 // returns size of remaining buffer
4685 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr) {
4686 const char *aptr, *ptr, *eptr;
4689 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL) || (LineBuf == NULL)|| (LineBuf->buf == NULL)) {
4690 *Ptr = StrBufNOTNULL;
4694 FlushStrBuf(LineBuf);
4696 ptr = aptr = Buf->buf;
4700 optr = LineBuf->buf;
4701 eptr = Buf->buf + Buf->BufUsed;
4702 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4704 while ((ptr <= eptr) && (*ptr != '\n') && (*ptr != '\r') ) {
4708 LineBuf->BufUsed = optr - LineBuf->buf;
4709 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4710 optr = LineBuf->buf + LineBuf->BufUsed;
4711 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4715 if ((ptr >= eptr) && (optr > LineBuf->buf))
4717 LineBuf->BufUsed = optr - LineBuf->buf;
4719 if ((ptr <= eptr) && (*ptr == '\r'))
4721 if ((ptr <= eptr) && (*ptr == '\n'))
4728 *Ptr = StrBufNOTNULL;
4731 return Buf->BufUsed - (ptr - Buf->buf);
4735 // removes double slashes from pathnames
4736 // Dir directory string to filter
4737 // RemoveTrailingSlash allows / disallows trailing slashes
4738 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash) {
4743 while (!IsEmptyStr(a)) {
4755 if ((RemoveTrailingSlash) &&
4761 Dir->BufUsed = b - Dir->buf;
4765 // Decode a quoted-printable encoded StrBuf buffer "in place"
4766 // This is possible because the decoded will always be shorter than the encoded
4767 // so we don't have to worry about the buffer being to small.
4768 void StrBufDecodeQP(StrBuf *Buf) {
4769 if (!Buf) { // sanity check #1
4773 int source_len = StrLength(Buf);
4774 if (source_len < 1) { // sanity check #2
4778 int spos = 0; // source position
4779 int tpos = 0; // target position
4781 while (spos < source_len) {
4782 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
4785 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
4788 else if (Buf->buf[spos] == '=') {
4791 sscanf(&Buf->buf[spos], "%02x", &ch);
4792 Buf->buf[tpos++] = ch;
4796 Buf->buf[tpos++] = Buf->buf[spos++];
4801 Buf->BufUsed = tpos;