1 // Copyright (c) 1987-2024 by the citadel.org team
3 // This program is open source software. Use, duplication, or disclosure is subject to the GNU General Public License, version 3.
13 #include <sys/select.h>
15 #include <sys/types.h>
16 #define SHOW_ME_VAPPEND_PRINTF
19 #include "libcitadel.h"
35 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen, const Bytef * source, uLong sourceLen, int level);
37 int BaseStrBufSize = 64;
39 int ZLibCompressionRatio = -1; /* defaults to 6 */
41 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
42 #define OS_CODE 0x03 /*< unix */
43 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
46 const char *StrBufNOTNULL = ((char*) NULL) - 1;
48 const char HexList[256][3] = {
49 "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
50 "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
51 "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
52 "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
53 "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
54 "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
55 "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
56 "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
57 "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
58 "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
59 "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
60 "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
61 "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
62 "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
63 "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
64 "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
67 // Private Structure for the Stringbuffer
69 char *buf; // the pointer to the dynamic buffer
70 long BufSize; // how many spcae do we optain
71 long BufUsed; // Number of Chars used excluding the trailing \\0
72 int ConstBuf; // are we just a wrapper arround a static buffer and musn't we be changed?
74 long nIncreases; // for profiling; cound how many times we needed more
75 char bt [SIZ]; // Stacktrace of last increase
76 char bt_lastinc[SIZ]; // How much did we increase last time?
81 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
82 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
86 static void StrBufBacktrace(StrBuf *Buf, int which) {
89 void *stack_frames[50];
94 pstart = pch = Buf->bt;
97 pstart = pch = Buf->bt_lastinc;
99 size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
100 strings = backtrace_symbols(stack_frames, size);
101 for (i = 0; i < size; i++) {
102 if (strings != NULL) {
103 n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
106 n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
115 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere) {
116 if (hFreeDbglog == -1) {
117 pid_t pid = getpid();
119 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
120 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
122 if ((*FreeMe)->nIncreases > 0) {
125 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
127 (*FreeMe)->nIncreases,
131 (*FreeMe)->bt_lastinc
133 n = write(hFreeDbglog, buf, n);
138 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
143 n = write(hFreeDbglog, buf, n);
148 void dbg_IncreaseBuf(StrBuf *IncMe) {
150 #ifdef HAVE_BACKTRACE
151 StrBufBacktrace(Buf, 1);
156 void dbg_Init(StrBuf *Buf) {
159 Buf->bt_lastinc[0] = '\0';
160 #ifdef HAVE_BACKTRACE
161 StrBufBacktrace(Buf, 0);
167 #define dbg_FreeStrBuf(a, b)
168 #define dbg_IncreaseBuf(a)
173 // swaps the contents of two StrBufs
174 // this is to be used to have cheap switched between a work-buffer and a target buffer
177 static inline void iSwapBuffers(StrBuf *A, StrBuf *B) {
180 memcpy(&C, A, sizeof(*A));
181 memcpy(A, B, sizeof(*B));
182 memcpy(B, &C, sizeof(C));
187 void SwapBuffers(StrBuf *A, StrBuf *B) {
192 // Cast operator to Plain String
193 // note: if the buffer is altered by StrBuf operations, this pointer may become
194 // invalid. So don't lean on it after altering the buffer!
195 // Since this operation is considered cheap, rather call it often than risking
196 // your pointer to become invalid!
197 // Str the string we want to get the c-string representation for
198 // returns the Pointer to the Content. Don't mess with it!
199 inline const char *ChrPtr(const StrBuf *Str) {
206 // since we know strlen()'s result, provide it here.
207 // Str the string to return the length to
208 // returns contentlength of the buffer
209 inline int StrLength(const StrBuf *Str) {
210 return (Str != NULL) ? Str->BufUsed : 0;
214 // local utility function to resize the buffer
215 // Buf the buffer whichs storage we should increase
216 // KeepOriginal should we copy the original buffer or just start over with a new one
217 // DestSize what should fit in after?
218 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
220 size_t NewSize = Buf->BufSize * 2;
227 while ((NewSize <= DestSize) && (NewSize != 0)) {
236 NewBuf = (char*) malloc(NewSize);
237 if (NewBuf == NULL) {
241 if (KeepOriginal && (Buf->BufUsed > 0)) {
242 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
250 Buf->BufSize = NewSize;
252 dbg_IncreaseBuf(Buf);
258 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
259 // Buf Buffer to shrink (has to be empty)
260 // ThreshHold if the buffer is bigger then this, its readjusted
261 // NewSize if we Shrink it, how big are we going to be afterwards?
262 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
263 if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
265 Buf->buf = (char*) malloc(NewSize);
267 Buf->BufSize = NewSize;
272 // shrink long term buffers to their real size so they don't waste memory
273 // Buf buffer to shrink
274 // Force if not set, will just executed if the buffer is much to big; set for lifetime strings
275 // returns physical size of the buffer
276 long StrBufShrinkToFit(StrBuf *Buf, int Force) {
279 if (Force || (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize)) {
282 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
286 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
287 Buf->BufSize = Buf->BufUsed + 1;
295 // Allocate a new buffer with default buffer size
296 // returns the new stringbuffer
297 StrBuf *NewStrBuf(void) {
300 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
304 NewBuf->buf = (char*) malloc(BaseStrBufSize);
305 if (NewBuf->buf == NULL) {
309 NewBuf->buf[0] = '\0';
310 NewBuf->BufSize = BaseStrBufSize;
312 NewBuf->ConstBuf = 0;
319 // Copy Constructor; returns a duplicate of CopyMe
320 // CopyMe Buffer to faxmilate
321 // returns the new stringbuffer
322 StrBuf *NewStrBufDup(const StrBuf *CopyMe) {
328 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
332 NewBuf->buf = (char*) malloc(CopyMe->BufSize);
333 if (NewBuf->buf == NULL) {
338 memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
339 NewBuf->BufUsed = CopyMe->BufUsed;
340 NewBuf->BufSize = CopyMe->BufSize;
341 NewBuf->ConstBuf = 0;
349 // Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
350 // NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
351 // CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
352 // CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe
353 // KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
354 // returns the new stringbuffer
355 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal) {
358 if (CreateRelpaceMe == NULL)
362 if (*CreateRelpaceMe != NULL)
363 StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
365 *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
369 if (CopyFlushMe == NULL) {
370 if (*CreateRelpaceMe != NULL)
371 FlushStrBuf(*CreateRelpaceMe);
373 *CreateRelpaceMe = NewStrBuf();
377 // Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
378 // else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
379 // be a big IO-Buffer...
380 if (KeepOriginal || (StrLength(CopyFlushMe) < 256)) {
381 if (*CreateRelpaceMe == NULL) {
382 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
386 NewBuf = *CreateRelpaceMe;
389 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
392 if (*CreateRelpaceMe == NULL) {
393 *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
397 NewBuf = *CreateRelpaceMe;
399 iSwapBuffers (NewBuf, CopyFlushMe);
402 FlushStrBuf(CopyFlushMe);
408 // create a new Buffer using an existing c-string
409 // this function should also be used if you want to pre-suggest
410 // the buffer size to allocate in conjunction with ptr == NULL
411 // ptr the c-string to copy; may be NULL to create a blank instance
412 // nChars How many chars should we copy; -1 if we should measure the length ourselves
413 // returns the new stringbuffer
414 StrBuf *NewStrBufPlain(const char* ptr, int nChars) {
416 size_t Siz = BaseStrBufSize;
419 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
424 CopySize = strlen((ptr != NULL)?ptr:"");
428 while ((Siz <= CopySize) && (Siz != 0))
436 NewBuf->buf = (char*) malloc(Siz);
437 if (NewBuf->buf == NULL) {
441 NewBuf->BufSize = Siz;
443 memcpy(NewBuf->buf, ptr, CopySize);
444 NewBuf->buf[CopySize] = '\0';
445 NewBuf->BufUsed = CopySize;
448 NewBuf->buf[0] = '\0';
451 NewBuf->ConstBuf = 0;
460 // Set an existing buffer from a c-string
461 // Buf buffer to load
462 // ptr c-string to put into
463 // nChars set to -1 if we should work 0-terminated
464 // @returns the new length of the string
466 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars) {
480 CopySize = strlen(ptr);
484 while ((Siz <= CopySize) && (Siz != 0))
492 if (Siz != Buf->BufSize)
493 IncreaseBuf(Buf, 0, Siz);
494 memcpy(Buf->buf, ptr, CopySize);
495 Buf->buf[CopySize] = '\0';
496 Buf->BufUsed = CopySize;
503 // use strbuf as wrapper for a string constant for easy handling
504 // StringConstant a string to wrap
505 // SizeOfStrConstant should be sizeof(StringConstant)-1
507 StrBuf *_NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant) {
510 NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
513 NewBuf->buf = (char*) StringConstant;
514 NewBuf->BufSize = SizeOfStrConstant;
515 NewBuf->BufUsed = SizeOfStrConstant;
516 NewBuf->ConstBuf = 1;
525 // flush the content of a Buf; keep its struct
526 // buf Buffer to flush
528 int FlushStrBuf(StrBuf *buf) {
529 if ((buf == NULL) || (buf->buf == NULL)) {
541 // wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
542 // buf Buffer to wipe
544 int FLUSHStrBuf(StrBuf *buf) {
549 if (buf->BufUsed > 0) {
550 memset(buf->buf, 0, buf->BufUsed);
557 int hFreeDbglog = -1;
561 // Its a double pointer, so it can NULL your pointer
562 // so fancy SIG11 appear instead of random results
563 // FreeMe Pointer Pointer to the buffer to free
565 void FreeStrBuf (StrBuf **FreeMe) {
569 dbg_FreeStrBuf(FreeMe, 'F');
571 if (!(*FreeMe)->ConstBuf)
572 free((*FreeMe)->buf);
578 // flatten a Buffer to the Char * we return
579 // Its a double pointer, so it can NULL your pointer
580 // so fancy SIG11 appear instead of random results
581 // The Callee then owns the buffer and is responsible for freeing it.
582 // SmashMe Pointer Pointer to the buffer to release Buf from and free
583 // @returns the pointer of the buffer; Callee owns the memory thereafter.
585 char *SmashStrBuf (StrBuf **SmashMe) {
588 if ((SmashMe == NULL) || (*SmashMe == NULL))
591 dbg_FreeStrBuf(SmashMe, 'S');
593 Ret = (*SmashMe)->buf;
600 // Release the buffer
601 // If you want put your StrBuf into a Hash, use this as Destructor.
602 // VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
603 void HFreeStrBuf (void *VFreeMe) {
604 StrBuf *FreeMe = (StrBuf*)VFreeMe;
608 dbg_FreeStrBuf(SmashMe, 'H');
610 if (!FreeMe->ConstBuf)
616 //******************************************************************************
617 // Simple string transformations *
618 //******************************************************************************/
620 // Wrapper around atol
621 long StrTol(const StrBuf *Buf) {
625 return atol(Buf->buf);
631 // Wrapper around atoi
632 int StrToi(const StrBuf *Buf) {
635 if (Buf->BufUsed > 0)
636 return atoi(Buf->buf);
642 // Checks to see if the string is a pure number
643 // Buf The buffer to inspect
644 // returns 1 if its a pure number, 0, if not.
645 int StrBufIsNumber(const StrBuf *Buf) {
647 if ((Buf == NULL) || (Buf->BufUsed == 0)) {
650 strtoll(Buf->buf, &pEnd, 10);
651 if (pEnd == Buf->buf)
653 if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
655 if (Buf->buf == pEnd)
661 // modifies a Single char of the Buf
662 // You can point to it via char* or a zero-based integer
663 // Buf The buffer to manipulate
664 // ptr char* to zero; use NULL if unused
665 // nThChar zero based pointer into the string; use -1 if unused
666 // PeekValue The Character to place into the position
667 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue) {
671 nThChar = ptr - Buf->buf;
672 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
674 Buf->buf[nThChar] = PeekValue;
679 // modifies a range of chars of the Buf
680 // You can point to it via char* or a zero-based integer
681 // Buf The buffer to manipulate
682 // ptr char* to zero; use NULL if unused
683 // nThChar zero based pointer into the string; use -1 if unused
684 // nChars how many chars are to be flushed?
685 // PookValue The Character to place into that area
686 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue) {
690 nThChar = ptr - Buf->buf;
691 if ((nThChar < 0) || (nThChar > Buf->BufUsed))
693 if (nThChar + nChars > Buf->BufUsed)
694 nChars = Buf->BufUsed - nThChar;
696 memset(Buf->buf + nThChar, PookValue, nChars);
697 // just to be sure...
698 Buf->buf[Buf->BufUsed] = 0;
703 // Append a StringBuffer to the buffer
704 // Buf Buffer to modify
705 // AppendBuf Buffer to copy at the end of our buffer
706 // Offset Should we start copying from an offset?
707 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset) {
708 if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) || (Buf == NULL) || (Buf->buf == NULL)) {
712 if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
713 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
715 memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
716 Buf->BufUsed += AppendBuf->BufUsed - Offset;
717 Buf->buf[Buf->BufUsed] = '\0';
721 // Append a C-String to the buffer
722 // Buf Buffer to modify
723 // AppendBuf Buffer to copy at the end of our buffer
724 // AppendSize number of bytes to copy; set to -1 if we should count it in advance
725 // Offset Should we start copying from an offset?
726 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
728 long BufSizeRequired;
730 if ((AppendBuf == NULL) || (Buf == NULL))
733 if (AppendSize < 0) {
734 aps = strlen(AppendBuf + Offset);
737 aps = AppendSize - Offset;
740 BufSizeRequired = Buf->BufUsed + aps + 1;
741 if (Buf->BufSize <= BufSizeRequired) {
742 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
745 memcpy(Buf->buf + Buf->BufUsed, AppendBuf + Offset, aps);
747 Buf->buf[Buf->BufUsed] = '\0';
751 // sprintf like function appending the formated string to the buffer
752 // vsnprintf version to wrap into own calls
753 // Buf Buffer to extend by format and Params
754 // format printf alike format to add
755 // ap va_list containing the items for format
756 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap) {
763 if ((Buf == NULL) || (format == NULL))
766 BufSize = Buf->BufSize;
767 nWritten = Buf->BufSize + 1;
768 Offset = Buf->BufUsed;
769 newused = Offset + nWritten;
771 while (newused >= BufSize) {
773 nWritten = vsnprintf(Buf->buf + Offset, Buf->BufSize - Offset, format, apl);
775 newused = Offset + nWritten;
776 if (newused >= Buf->BufSize) {
777 if (IncreaseBuf(Buf, 1, newused) == -1)
778 return; // TODO: error handling?
779 newused = Buf->BufSize + 1;
782 Buf->BufUsed = Offset + nWritten;
783 BufSize = Buf->BufSize;
790 // sprintf like function appending the formated string to the buffer
791 // Buf Buffer to extend by format and Params
792 // format printf alike format to add
793 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...) {
800 if ((Buf == NULL) || (format == NULL))
803 BufSize = Buf->BufSize;
804 nWritten = Buf->BufSize + 1;
805 Offset = Buf->BufUsed;
806 newused = Offset + nWritten;
808 while (newused >= BufSize) {
809 va_start(arg_ptr, format);
810 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, Buf->BufSize - Buf->BufUsed, format, arg_ptr);
812 newused = Buf->BufUsed + nWritten;
813 if (newused >= Buf->BufSize) {
814 if (IncreaseBuf(Buf, 1, newused) == -1)
815 return; // TODO: error handling?
816 newused = Buf->BufSize + 1;
819 Buf->BufUsed += nWritten;
820 BufSize = Buf->BufSize;
827 // sprintf like function putting the formated string into the buffer
828 // Buf Buffer to extend by format and Parameters
829 // format printf alike format to add
830 void StrBufPrintf(StrBuf *Buf, const char *format, ...) {
834 if ((Buf == NULL) || (format == NULL))
837 nWritten = Buf->BufSize + 1;
838 while (nWritten >= Buf->BufSize) {
839 va_start(arg_ptr, format);
840 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
842 if (nWritten >= Buf->BufSize) {
843 if (IncreaseBuf(Buf, 0, 0) == -1)
844 return; // TODO: error handling?
845 nWritten = Buf->BufSize + 1;
848 Buf->BufUsed = nWritten ;
853 // Callback for cURL to append the webserver reply to a buffer
854 // ptr, size, nmemb, and stream are pre-defined by the cURL API; see man 3 curl for more info
855 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream) {
863 StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
868 // extracts a substring from Source into dest
869 // dest buffer to place substring into
870 // Source string to copy substring from
871 // Offset chars to skip from start
872 // nChars number of chars to copy
873 // returns the number of chars copied; may be different from nChars due to the size of Source
874 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars) {
876 if (Offset > Source->BufUsed) {
881 if (Offset + nChars < Source->BufUsed) {
882 if ((nChars >= dest->BufSize) && (IncreaseBuf(dest, 0, nChars + 1) == -1)) {
885 memcpy(dest->buf, Source->buf + Offset, nChars);
886 dest->BufUsed = nChars;
887 dest->buf[dest->BufUsed] = '\0';
890 NCharsRemain = Source->BufUsed - Offset;
891 if ((NCharsRemain >= dest->BufSize) && (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1)) {
894 memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
895 dest->BufUsed = NCharsRemain;
896 dest->buf[dest->BufUsed] = '\0';
900 // Cut nChars from the start of the string
901 // Buf Buffer to modify
902 // nChars how many chars should be skipped?
903 void StrBufCutLeft(StrBuf *Buf, int nChars) {
904 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
905 if (nChars >= Buf->BufUsed) {
909 memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
910 Buf->BufUsed -= nChars;
911 Buf->buf[Buf->BufUsed] = '\0';
914 // Cut the trailing n Chars from the string
915 // Buf Buffer to modify
916 // nChars how many chars should be trunkated?
917 void StrBufCutRight(StrBuf *Buf, int nChars) {
918 if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
921 if (nChars >= Buf->BufUsed) {
925 Buf->BufUsed -= nChars;
926 Buf->buf[Buf->BufUsed] = '\0';
930 // Cut the string after n Chars
931 // Buf Buffer to modify
932 // AfternChars after how many chars should we trunkate the string?
933 // At if non-null and points inside of our string, cut it there.
935 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
937 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
939 AfternChars = At - Buf->buf;
942 if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
944 Buf->BufUsed = AfternChars;
945 Buf->buf[Buf->BufUsed] = '\0';
950 // Strip leading and trailing spaces from a string; with premeasured and adjusted length.
951 // Buf the string to modify
953 void StrBufTrim(StrBuf *Buf)
956 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
958 while ((Buf->BufUsed > 0) && isspace(Buf->buf[Buf->BufUsed - 1])) {
961 Buf->buf[Buf->BufUsed] = '\0';
963 if (Buf->BufUsed == 0) return;
965 while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
968 if (delta > 0) StrBufCutLeft(Buf, delta);
971 // changes all spaces in the string (tab, linefeed...) to Blank (0x20)
972 // Buf the string to modify
974 void StrBufSpaceToBlank(StrBuf *Buf)
978 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
981 pche = pch + Buf->BufUsed;
990 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
995 if ((Buf == NULL) || (Buf->buf == NULL)) {
999 pRight = strchr(Buf->buf, rightboundary);
1000 if (pRight != NULL) {
1001 StrBufCutAt(Buf, 0, pRight);
1004 pLeft = strrchr(ChrPtr(Buf), leftboundary);
1005 if (pLeft != NULL) {
1006 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1012 // uppercase the contents of a buffer
1013 // Buf the buffer to translate
1015 void StrBufUpCase(StrBuf *Buf)
1019 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1022 pche = pch + Buf->BufUsed;
1023 while (pch < pche) {
1024 *pch = toupper(*pch);
1031 // lowercase the contents of a buffer
1032 // Buf the buffer to translate
1034 void StrBufLowerCase(StrBuf *Buf)
1038 if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1041 pche = pch + Buf->BufUsed;
1042 while (pch < pche) {
1043 *pch = tolower(*pch);
1049 //******************************************************************************
1050 // a tokenizer that kills, maims, and destroys *
1051 //******************************************************************************/
1054 // Replace a token at a given place with a given length by another token with given length
1055 // Buf String where to work on
1056 // where where inside of the Buf is the search-token
1057 // HowLong How long is the token to be replaced
1058 // Repl Token to insert at 'where'
1059 // ReplLen Length of repl
1060 // @returns -1 if fail else length of resulting Buf
1062 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, const char *Repl, long ReplLen) {
1064 if ((Buf == NULL) || (where > Buf->BufUsed) || (where + HowLong > Buf->BufUsed)) {
1068 if (where + ReplLen - HowLong > Buf->BufSize) {
1069 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0) {
1074 memmove(Buf->buf + where + ReplLen, Buf->buf + where + HowLong, Buf->BufUsed - where - HowLong);
1075 memcpy(Buf->buf + where, Repl, ReplLen);
1076 Buf->BufUsed += ReplLen - HowLong;
1077 Buf->buf[Buf->BufUsed] = 0;
1078 return Buf->BufUsed;
1082 // Counts the numbmer of tokens in a buffer
1083 // source String to count tokens in
1084 // tok Tokenizer char to count
1085 // @returns numbers of tokenizer chars found
1087 int StrBufNum_tokens(const StrBuf *source, char tok) {
1090 if ((source == NULL) || (source->BufUsed == 0))
1092 if ((source->BufUsed == 1) && (*source->buf == tok))
1096 pche = pch + source->BufUsed;
1107 // a string tokenizer
1108 // Source StringBuffer to read into
1109 // parmnum n'th Parameter to remove
1110 // separator tokenizer character
1111 // @returns -1 if not found, else length of token.
1113 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1116 char *d, *s, *end; /* dest, source */
1119 /* Find desired eter */
1120 end = Source->buf + Source->BufUsed;
1122 while ((d <= end) && (count < parmnum)) {
1123 /* End of string, bail! */
1128 if (*d == separator) {
1133 if ((d == NULL) || (d >= end))
1134 return 0; /* @Parameter not found */
1136 /* Find next eter */
1138 while ((s <= end) && (*s && *s != separator)) {
1141 if (*s == separator)
1145 /* Hack and slash */
1150 memmove(d, s, Source->BufUsed - (s - Source->buf));
1151 Source->BufUsed += ReducedBy;
1152 Source->buf[Source->BufUsed] = '\0';
1154 else if (d == Source->buf) {
1156 Source->BufUsed = 0;
1160 Source->BufUsed += ReducedBy;
1171 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1173 const StrBuf Temp = {
1186 return StrBufExtract_token(dest, &Temp, parmnum, separator);
1190 // a string tokenizer
1191 // dest Destination StringBuffer
1192 // Source StringBuffer to read into
1193 // parmnum n'th Parameter to extract
1194 // separator tokenizer character
1195 // @returns -1 if not found, else length of token.
1197 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1199 const char *s, *e; // source
1200 int len = 0; // running total length of extracted string
1201 int current_token = 0; // token currently being processed
1204 dest->buf[0] = '\0';
1210 if ((Source == NULL) || (Source->BufUsed ==0)) {
1214 e = s + Source->BufUsed;
1217 //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1219 while ((s < e) && !IsEmptyStr(s)) {
1220 if (*s == separator) {
1223 if (len >= dest->BufSize) {
1224 dest->BufUsed = len;
1225 if (IncreaseBuf(dest, 1, -1) < 0) {
1230 if ( (current_token == parmnum) && (*s != separator)) {
1231 dest->buf[len] = *s;
1234 else if (current_token > parmnum) {
1240 dest->buf[len] = '\0';
1241 dest->BufUsed = len;
1243 if (current_token < parmnum) {
1244 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1247 //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1256 * a string tokenizer to fetch an integer
1257 * Source String containing tokens
1258 * parmnum n'th Parameter to extract
1259 * separator tokenizer character
1260 * @returns 0 if not found, else integer representation of the token
1262 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1272 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1279 * a string tokenizer to fetch a long integer
1280 * Source String containing tokens
1281 * parmnum n'th Parameter to extract
1282 * separator tokenizer character
1283 * @returns 0 if not found, else long integer representation of the token
1285 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1295 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1303 * a string tokenizer to fetch an unsigned long
1304 * Source String containing tokens
1305 * parmnum n'th Parameter to extract
1306 * separator tokenizer character
1307 * @returns 0 if not found, else unsigned long representation of the token
1309 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1320 if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1324 return (unsigned long) atol(pnum);
1333 * a string tokenizer; Bounds checker
1334 * function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1335 * Source our tokenbuffer
1336 * pStart the token iterator pointer to inspect
1337 * @returns whether the revolving pointer is inside of the search range
1339 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart) {
1340 if ((Source == NULL) || (*pStart == StrBufNOTNULL) || (Source->BufUsed == 0)) {
1343 if (*pStart == NULL) {
1346 else if (*pStart > Source->buf + Source->BufUsed) {
1349 else if (*pStart <= Source->buf) {
1357 * a string tokenizer
1358 * dest Destination StringBuffer
1359 * Source StringBuffer to read into
1360 * pStart pointer to the end of the last token. Feed with NULL on start.
1361 * separator tokenizer
1362 * @returns -1 if not found, else length of token.
1364 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator) {
1365 const char *s; /* source */
1366 const char *EndBuffer; /* end stop of source buffer */
1367 int current_token = 0; /* token currently being processed */
1368 int len = 0; /* running total length of extracted string */
1370 if ((Source == NULL) || (Source->BufUsed == 0)) {
1371 *pStart = StrBufNOTNULL;
1378 EndBuffer = Source->buf + Source->BufUsed;
1381 dest->buf[0] = '\0';
1385 *pStart = EndBuffer + 1;
1389 if (*pStart == NULL) {
1390 *pStart = Source->buf; /* we're starting to examine this buffer. */
1392 else if ((*pStart < Source->buf) || (*pStart > EndBuffer ) ) {
1393 return -1; /* no more tokens to find. */
1397 /* start to find the next token */
1398 while ((s <= EndBuffer) && (current_token == 0) ) {
1399 if (*s == separator) {
1400 /* we found the next token */
1404 if (len >= dest->BufSize) {
1405 /* our Dest-buffer isn't big enough, increase it. */
1406 dest->BufUsed = len;
1408 if (IncreaseBuf(dest, 1, -1) < 0) {
1409 /* WHUT? no more mem? bail out. */
1416 if ( (current_token == 0 ) && /* are we in our target token? */
1417 (!IsEmptyStr(s) ) &&
1418 (separator != *s) ) /* don't copy the token itself */
1420 dest->buf[len] = *s; /* Copy the payload */
1421 ++len; /* remember the bigger size. */
1427 /* did we reach the end? */
1428 if ((s > EndBuffer)) {
1429 EndBuffer = StrBufNOTNULL;
1430 *pStart = EndBuffer;
1433 *pStart = s; /* remember the position for the next run */
1436 /* sanitize our extracted token */
1437 dest->buf[len] = '\0';
1438 dest->BufUsed = len;
1445 * a string tokenizer
1446 * Source StringBuffer to read from
1447 * pStart pointer to the end of the last token. Feed with NULL.
1448 * separator tokenizer character
1449 * nTokens number of tokens to fastforward over
1450 * @returns -1 if not found, else length of token.
1452 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens) {
1453 const char *s, *EndBuffer; //* source * /
1454 int len = 0; //* running total length of extracted string * /
1455 int current_token = 0; //* token currently being processed * /
1457 if ((Source == NULL) || (Source->BufUsed ==0)) {
1461 return Source->BufUsed;
1463 if (*pStart == NULL)
1464 *pStart = Source->buf;
1466 EndBuffer = Source->buf + Source->BufUsed;
1468 if ((*pStart < Source->buf) ||
1469 (*pStart > EndBuffer)) {
1475 while ((s < EndBuffer) && !IsEmptyStr(s)) {
1476 if (*s == separator) {
1479 if (current_token >= nTokens) {
1491 // a string tokenizer to fetch an integer
1492 // Source StringBuffer to read from
1493 // pStart Cursor on the tokenstring
1494 // separator tokenizer character
1495 // returns 0 if not found, else integer representation of the token
1496 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator) {
1505 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1512 // a string tokenizer to fetch a long integer
1513 // Source StringBuffer to read from
1514 // pStart Cursor on the tokenstring
1515 // separator tokenizer character
1516 // returns 0 if not found, else long integer representation of the token
1517 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator) {
1526 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1533 // a string tokenizer to fetch an unsigned long
1534 // Source StringBuffer to read from
1535 // pStart Cursor on the tokenstring
1536 // separator tokenizer character
1537 // returns 0 if not found, else unsigned long representation of the token
1538 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator) {
1548 if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1552 return (unsigned long) atol(pnum);
1559 /*******************************************************************************
1560 * Escape Appending *
1561 *******************************************************************************/
1563 // Escape a string for feeding out as a URL while appending it to a Buffer
1564 // OutBuf the output buffer
1565 // In Buffer to encode
1566 // PlainIn way in from plain old c strings
1567 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1568 const char *pch, *pche;
1572 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) ) {
1575 if (PlainIn != NULL) {
1576 len = strlen(PlainIn);
1582 pche = pch + In->BufUsed;
1589 pt = OutBuf->buf + OutBuf->BufUsed;
1590 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1592 while (pch < pche) {
1594 IncreaseBuf(OutBuf, 1, -1);
1595 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1596 pt = OutBuf->buf + OutBuf->BufUsed;
1599 if((*pch >= 'a' && *pch <= 'z') ||
1600 (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1601 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1602 (*pch == '!') || (*pch == '_') ||
1603 (*pch == ',') || (*pch == '.'))
1610 *(pt + 1) = HexList[(unsigned char)*pch][0];
1611 *(pt + 2) = HexList[(unsigned char)*pch][1];
1613 OutBuf->BufUsed += 3;
1621 // Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1622 // OutBuf the output buffer
1623 // In Buffer to encode
1624 // PlainIn way in from plain old c strings
1625 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1626 const char *pch, *pche;
1630 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1632 if (PlainIn != NULL) {
1633 len = strlen(PlainIn);
1639 pche = pch + In->BufUsed;
1646 pt = OutBuf->buf + OutBuf->BufUsed;
1647 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1649 while (pch < pche) {
1651 IncreaseBuf(OutBuf, 1, -1);
1652 pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1653 pt = OutBuf->buf + OutBuf->BufUsed;
1656 if((*pch >= 'a' && *pch <= 'z') ||
1657 (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1658 (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1659 (*pch == '!') || (*pch == '_') ||
1660 (*pch == ',') || (*pch == '.'))
1667 *(pt + 1) = HexList[(unsigned char)*pch][0];
1668 *(pt + 2) = HexList[(unsigned char)*pch][1];
1670 OutBuf->BufUsed += 3;
1678 // append a string with characters having a special meaning in xml encoded to the buffer
1679 // OutBuf the output buffer
1680 // In Buffer to encode
1681 // PlainIn way in from plain old c strings
1682 // PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1683 // OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1684 void StrBufXMLEscAppend(StrBuf *OutBuf,
1686 const char *PlainIn,
1688 int OverrideLowChars)
1690 const char *pch, *pche;
1695 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1697 if (PlainIn != NULL) {
1699 len = strlen((const char*)PlainIn);
1706 pch = (const char*)In->buf;
1707 pche = pch + In->BufUsed;
1714 pt = OutBuf->buf + OutBuf->BufUsed;
1715 /**< we max append 6 chars at once plus the \0 */
1716 pte = OutBuf->buf + OutBuf->BufSize - 6;
1718 while (pch < pche) {
1720 OutBuf->BufUsed = pt - OutBuf->buf;
1721 IncreaseBuf(OutBuf, 1, -1);
1722 pte = OutBuf->buf + OutBuf->BufSize - 6;
1723 /**< we max append 3 chars at once plus the \0 */
1725 pt = OutBuf->buf + OutBuf->BufUsed;
1729 memcpy(pt, HKEY("<"));
1733 else if (*pch == '>') {
1734 memcpy(pt, HKEY(">"));
1738 else if (*pch == '&') {
1739 memcpy(pt, HKEY("&"));
1743 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
1748 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(pch, pche);
1751 while ((IsUtf8Sequence > 0) &&
1764 *pt = HexList[*(unsigned char*)pch][0];
1766 *pt = HexList[*(unsigned char*)pch][1];
1775 OutBuf->BufUsed = pt - OutBuf->buf;
1780 * append a string in hex encoding to the buffer
1781 * OutBuf the output buffer
1782 * In Buffer to encode
1783 * PlainIn way in from plain old c strings
1784 * PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1786 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
1788 const unsigned char *pch, *pche;
1792 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1794 if (PlainIn != NULL) {
1796 len = strlen((const char*)PlainIn);
1803 pch = (const unsigned char*)In->buf;
1804 pche = pch + In->BufUsed;
1811 pt = OutBuf->buf + OutBuf->BufUsed;
1812 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1814 while (pch < pche) {
1816 IncreaseBuf(OutBuf, 1, -1);
1817 pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
1818 pt = OutBuf->buf + OutBuf->BufUsed;
1821 *pt = HexList[*pch][0];
1823 *pt = HexList[*pch][1];
1824 pt ++; pch ++; OutBuf->BufUsed += 2;
1830 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks) {
1836 if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1838 if (PlainIn != NULL) {
1840 len = strlen(PlainIn);
1853 ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
1855 if (ExpectLen > OutBuf->BufSize)
1856 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
1859 pt = OutBuf->buf + OutBuf->BufUsed;
1861 len = CtdlEncodeBase64(pt, pch, len, linebreaks);
1864 OutBuf->BufUsed += len;
1869 // append a string in hex encoding to the buffer
1870 // OutBuf the output buffer
1871 // In Buffer to encode
1872 // PlainIn way in from plain old c strings
1873 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn) {
1874 StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
1878 * Append a string, escaping characters which have meaning in HTML.
1880 * Target target buffer
1881 * Source source buffer; set to NULL if you just have a C-String
1882 * PlainIn Plain-C string to append; set to NULL if unused
1883 * nbsp If nonzero, spaces are converted to non-breaking spaces.
1884 * nolinebreaks if set to 1, linebreaks are removed from the string.
1885 * if set to 2, linebreaks are replaced by <br/>
1887 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
1889 const char *aptr, *eiptr;
1893 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
1896 if (PlainIn != NULL) {
1898 len = strlen(PlainIn);
1903 eiptr = aptr + Source->BufUsed;
1904 len = Source->BufUsed;
1910 bptr = Target->buf + Target->BufUsed;
1911 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1913 while (aptr < eiptr){
1915 IncreaseBuf(Target, 1, -1);
1916 eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */
1917 bptr = Target->buf + Target->BufUsed;
1920 memcpy(bptr, "<", 4);
1922 Target->BufUsed += 4;
1924 else if (*aptr == '>') {
1925 memcpy(bptr, ">", 4);
1927 Target->BufUsed += 4;
1929 else if (*aptr == '&') {
1930 memcpy(bptr, "&", 5);
1932 Target->BufUsed += 5;
1934 else if (*aptr == '"') {
1935 memcpy(bptr, """, 6);
1937 Target->BufUsed += 6;
1939 else if (*aptr == '\'') {
1940 memcpy(bptr, "'", 5);
1942 Target->BufUsed += 5;
1944 else if (*aptr == LB) {
1949 else if (*aptr == RB) {
1954 else if (*aptr == QU) {
1959 else if ((*aptr == 32) && (nbsp == 1)) {
1960 memcpy(bptr, " ", 6);
1962 Target->BufUsed += 6;
1964 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
1965 *bptr='\0'; /* nothing */
1967 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
1968 memcpy(bptr, "<br/>", 11);
1970 Target->BufUsed += 11;
1974 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
1975 *bptr='\0'; /* nothing */
1985 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
1987 return Target->BufUsed;
1991 * Append a string, escaping characters which have meaning in HTML.
1992 * Converts linebreaks into blanks; escapes single quotes
1993 * Target target buffer
1994 * Source source buffer; set to NULL if you just have a C-String
1995 * PlainIn Plain-C string to append; set to NULL if unused
1997 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
1999 const char *aptr, *eiptr;
2003 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2006 if (PlainIn != NULL) {
2008 len = strlen(PlainIn);
2013 eiptr = aptr + Source->BufUsed;
2014 len = Source->BufUsed;
2020 eptr = Target->buf + Target->BufSize - 8;
2021 tptr = Target->buf + Target->BufUsed;
2023 while (aptr < eiptr){
2025 IncreaseBuf(Target, 1, -1);
2026 eptr = Target->buf + Target->BufSize - 8;
2027 tptr = Target->buf + Target->BufUsed;
2030 if (*aptr == '\n') {
2034 else if (*aptr == '\r') {
2038 else if (*aptr == '\'') {
2044 Target->BufUsed += 5;
2057 * Append a string, escaping characters which have meaning in ICAL.
2059 * Target target buffer
2060 * Source source buffer; set to NULL if you just have a C-String
2061 * PlainIn Plain-C string to append; set to NULL if unused
2063 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2065 const char *aptr, *eiptr;
2069 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2072 if (PlainIn != NULL) {
2074 len = strlen(PlainIn);
2079 eiptr = aptr + Source->BufUsed;
2080 len = Source->BufUsed;
2086 eptr = Target->buf + Target->BufSize - 8;
2087 tptr = Target->buf + Target->BufUsed;
2089 while (aptr < eiptr){
2090 if(tptr + 3 >= eptr) {
2091 IncreaseBuf(Target, 1, -1);
2092 eptr = Target->buf + Target->BufSize - 8;
2093 tptr = Target->buf + Target->BufUsed;
2096 if (*aptr == '\n') {
2103 else if (*aptr == '\r') {
2110 else if (*aptr == ',') {
2126 * Append a string, escaping characters which have meaning in JavaScript strings .
2128 * Target target buffer
2129 * Source source buffer; set to NULL if you just have a C-String
2130 * PlainIn Plain-C string to append; set to NULL if unused
2131 * @returns size of result or -1
2133 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2135 const char *aptr, *eiptr;
2140 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2143 if (PlainIn != NULL) {
2145 len = strlen(PlainIn);
2150 eiptr = aptr + Source->BufUsed;
2151 len = Source->BufUsed;
2157 bptr = Target->buf + Target->BufUsed;
2158 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2160 while (aptr < eiptr){
2162 IncreaseBuf(Target, 1, -1);
2163 eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */
2164 bptr = Target->buf + Target->BufUsed;
2168 memcpy(bptr, HKEY("\\n"));
2170 Target->BufUsed += 2;
2173 memcpy(bptr, HKEY("\\r"));
2175 Target->BufUsed += 2;
2182 Target->BufUsed += 2;
2185 if ((*(aptr + 1) == 'u') &&
2186 isxdigit(*(aptr + 2)) &&
2187 isxdigit(*(aptr + 3)) &&
2188 isxdigit(*(aptr + 4)) &&
2189 isxdigit(*(aptr + 5)))
2190 { /* oh, a unicode escaper. let it pass through. */
2191 memcpy(bptr, aptr, 6);
2194 Target->BufUsed += 6;
2202 Target->BufUsed += 2;
2210 Target->BufUsed += 2;
2217 Target->BufUsed += 2;
2224 Target->BufUsed += 2;
2227 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2228 while (IsUtf8Sequence > 0){
2231 if (--IsUtf8Sequence)
2239 if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2241 return Target->BufUsed;
2244 // Append a string, escaping characters which have meaning in HTML + json.
2246 // Target target buffer
2247 // Source source buffer; set to NULL if you just have a C-String
2248 // PlainIn Plain-C string to append; set to NULL if unused
2249 // nbsp If nonzero, spaces are converted to non-breaking spaces.
2250 // nolinebreaks if set to 1, linebreaks are removed from the string.
2251 // if set to 2, linebreaks are replaced by <br/>
2252 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks) {
2253 const char *aptr, *eiptr;
2256 int IsUtf8Sequence = 0;
2258 if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL)) {
2261 if (PlainIn != NULL) {
2263 len = strlen(PlainIn);
2268 eiptr = aptr + Source->BufUsed;
2269 len = Source->BufUsed;
2276 bptr = Target->buf + Target->BufUsed;
2277 eptr = Target->buf + Target->BufSize - 11; // our biggest unit to put in...
2279 while (aptr < eiptr) {
2281 IncreaseBuf(Target, 1, -1);
2282 eptr = Target->buf + Target->BufSize - 11; // our biggest unit to put in...
2283 bptr = Target->buf + Target->BufUsed;
2287 memcpy(bptr, HKEY("<"));
2289 Target->BufUsed += 4;
2292 memcpy(bptr, HKEY(">"));
2294 Target->BufUsed += 4;
2297 memcpy(bptr, HKEY("&"));
2299 Target->BufUsed += 5;
2312 switch (nolinebreaks) {
2314 *bptr='\0'; /* nothing */
2317 memcpy(bptr, HKEY("<br/>"));
2319 Target->BufUsed += 11;
2322 memcpy(bptr, HKEY("\\n"));
2324 Target->BufUsed += 2;
2328 switch (nolinebreaks) {
2331 *bptr='\0'; /* nothing */
2334 memcpy(bptr, HKEY("\\r"));
2336 Target->BufUsed += 2;
2346 Target->BufUsed += 2;
2349 if ((*(aptr + 1) == 'u') &&
2350 isxdigit(*(aptr + 2)) &&
2351 isxdigit(*(aptr + 3)) &&
2352 isxdigit(*(aptr + 4)) &&
2353 isxdigit(*(aptr + 5)))
2354 { /* oh, a unicode escaper. let it pass through. */
2355 memcpy(bptr, aptr, 6);
2358 Target->BufUsed += 6;
2365 Target->BufUsed += 2;
2373 Target->BufUsed += 2;
2380 Target->BufUsed += 2;
2387 Target->BufUsed += 2;
2391 memcpy(bptr, HKEY(" "));
2393 Target->BufUsed += 6;
2397 IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2398 while (IsUtf8Sequence > 0){
2401 if (--IsUtf8Sequence)
2409 if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2411 return Target->BufUsed;
2416 * replace all non-Ascii characters by another
2417 * Buf buffer to inspect
2418 * repl charater to stamp over non ascii chars
2420 void StrBufAsciify(StrBuf *Buf, const char repl) {
2423 for (offset = 0; offset < Buf->BufUsed; offset ++)
2424 if (!isascii(Buf->buf[offset]))
2425 Buf->buf[offset] = repl;
2430 // unhide special chars hidden to the HTML escaper
2431 // target buffer to put the unescaped string in
2432 // source buffer to unescape
2433 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) {
2437 if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2442 FlushStrBuf(target);
2444 len = source->BufUsed;
2445 for (a = 0; a < len; ++a) {
2446 if (target->BufUsed >= target->BufSize)
2447 IncreaseBuf(target, 1, -1);
2449 if (source->buf[a] == '=') {
2450 hex[0] = source->buf[a + 1];
2451 hex[1] = source->buf[a + 2];
2454 sscanf(hex, "%02x", &b);
2455 target->buf[target->BufUsed] = b;
2456 target->buf[++target->BufUsed] = 0;
2460 target->buf[target->BufUsed] = source->buf[a];
2461 target->buf[++target->BufUsed] = 0;
2468 * hide special chars from the HTML escapers and friends
2469 * target buffer to put the escaped string in
2470 * source buffer to escape
2472 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) {
2476 FlushStrBuf(target);
2478 if ((source == NULL) || (target == NULL) || (target->buf == NULL)) {
2482 len = source->BufUsed;
2483 for (i=0; i<len; ++i) {
2484 if (target->BufUsed + 4 >= target->BufSize)
2485 IncreaseBuf(target, 1, -1);
2486 if ( (isalnum(source->buf[i])) ||
2487 (source->buf[i]=='-') ||
2488 (source->buf[i]=='_') ) {
2489 target->buf[target->BufUsed++] = source->buf[i];
2492 sprintf(&target->buf[target->BufUsed],
2494 (0xFF &source->buf[i]));
2495 target->BufUsed += 3;
2498 target->buf[target->BufUsed + 1] = '\0';
2502 /*******************************************************************************
2503 * Quoted Printable de/encoding *
2504 *******************************************************************************/
2507 * decode a buffer from base 64 encoding; destroys original
2508 * Buf Buffor to transform
2510 int StrBufDecodeBase64(StrBuf *Buf) {
2518 xferbuf = (char*) malloc(Buf->BufSize);
2519 if (xferbuf == NULL)
2523 siz = CtdlDecodeBase64(xferbuf, Buf->buf, Buf->BufUsed);
2528 Buf->buf[Buf->BufUsed] = '\0';
2534 * decode a buffer from base 64 encoding; expects targetbuffer
2535 * BufIn Buffor to transform
2536 * BufOut Buffer to put result into
2538 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut) {
2539 if ((BufIn == NULL) || (BufOut == NULL))
2542 if (BufOut->BufSize < BufIn->BufUsed) {
2543 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2546 BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf, BufIn->buf, BufIn->BufUsed);
2547 return BufOut->BufUsed;
2550 typedef struct __z_enc_stream {
2556 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err) {
2563 z_enc_stream *stream;
2566 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2567 memset(stream, 0, sizeof(z_enc_stream));
2568 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2569 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2571 err = inflateInit(&stream->zstream);
2574 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2578 return (vStreamT*) stream;
2583 z_enc_stream *stream;
2586 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2587 memset(stream, 0, sizeof(z_enc_stream));
2588 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2589 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2590 /* write gzip header */
2591 stream->OutBuf.BufUsed = snprintf
2592 (stream->OutBuf.buf,
2593 stream->OutBuf.BufSize,
2594 "%c%c%c%c%c%c%c%c%c%c",
2595 gz_magic[0], gz_magic[1], Z_DEFLATED,
2596 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2599 err = deflateInit2(&stream->zstream,
2600 ZLibCompressionRatio,
2604 Z_DEFAULT_STRATEGY);
2606 StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2610 return (vStreamT*) stream;
2621 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err) {
2626 if ((vStream == NULL) || (*vStream==NULL)) {
2627 *Err = strerror(EINVAL);
2632 //case eBase64Encode:
2633 //case eBase64Decode:
2639 z_enc_stream *stream = (z_enc_stream *)*vStream;
2640 (void)inflateEnd(&stream->zstream);
2641 free(stream->OutBuf.buf);
2646 z_enc_stream *stream = (z_enc_stream *)*vStream;
2647 err = deflateEnd(&stream->zstream);
2652 free(stream->OutBuf.buf);
2663 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err) {
2667 //case eBase64Encode:
2672 //case eBase64Decode:
2679 z_enc_stream *stream = (z_enc_stream *)vStream;
2680 int org_outbuf_len = stream->OutBuf.BufUsed;
2682 unsigned int chunkavail;
2684 if (In->ReadWritePointer != NULL)
2686 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2687 stream->zstream.avail_in = (uInt) In->Buf->BufUsed -
2688 (In->ReadWritePointer - In->Buf->buf);
2692 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2693 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2696 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2697 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2699 err = deflate(&stream->zstream, (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
2701 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
2703 if (Target && (LastChunk || (stream->OutBuf.BufUsed != org_outbuf_len))) {
2704 iSwapBuffers(Target->Buf, &stream->OutBuf);
2707 if (stream->zstream.avail_in == 0) {
2708 FlushStrBuf(In->Buf);
2709 In->ReadWritePointer = NULL;
2712 if (stream->zstream.avail_in < 64) {
2713 memmove(In->Buf->buf,
2714 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2715 stream->zstream.avail_in);
2717 In->Buf->BufUsed = stream->zstream.avail_in;
2718 In->Buf->buf[In->Buf->BufUsed] = '\0';
2721 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2724 rc = (LastChunk && (err != Z_FINISH));
2725 if (!rc && (err != Z_OK)) {
2732 z_enc_stream *stream = (z_enc_stream *)vStream;
2733 int org_outbuf_len = stream->zstream.total_out;
2736 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
2737 if (In->ReadWritePointer != NULL) {
2738 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
2739 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - (In->ReadWritePointer - In->Buf->buf);
2742 stream->zstream.next_in = (Bytef *) In->Buf->buf;
2743 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
2747 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
2748 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
2750 err = inflate(&stream->zstream, Z_NO_FLUSH);
2752 ///assert(ret != Z_STREAM_ERROR); /* state not clobbered * /
2755 err = Z_DATA_ERROR; /* and fall through */
2760 (void)inflateEnd(&stream->zstream);
2764 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
2766 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
2768 if (stream->zstream.avail_in == 0) {
2769 FlushStrBuf(In->Buf);
2770 In->ReadWritePointer = NULL;
2773 if (stream->zstream.avail_in < 64) {
2774 memmove(In->Buf->buf,
2775 In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
2776 stream->zstream.avail_in);
2778 In->Buf->BufUsed = stream->zstream.avail_in;
2779 In->Buf->buf[In->Buf->BufUsed] = '\0';
2783 In->ReadWritePointer = In->Buf->buf + (In->Buf->BufUsed - stream->zstream.avail_in);
2797 * decode a buffer from base 64 encoding; destroys original
2798 * Buf Buffor to transform
2800 int StrBufDecodeHex(StrBuf *Buf) {
2802 char *pch, *pche, *pchi;
2804 if (Buf == NULL) return -1;
2806 pch = pchi = Buf->buf;
2807 pche = pch + Buf->BufUsed;
2809 while (pchi < pche){
2810 ch = decode_hex(pchi);
2817 Buf->BufUsed = pch - Buf->buf;
2818 return Buf->BufUsed;
2822 * replace all chars >0x20 && < 0x7F with Mute
2823 * Mute char to put over invalid chars
2824 * Buf Buffor to transform
2826 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
2830 if (Buf == NULL) return -1;
2831 pch = (unsigned char *)Buf->buf;
2832 while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
2833 if ((*pch < 0x20) || (*pch > 0x7F))
2837 return Buf->BufUsed;
2842 * remove escaped strings from i.e. the url string (like %20 for blanks)
2843 * Buf Buffer to translate
2844 * StripBlanks Reduce several blanks to one?
2846 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
2855 while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
2856 Buf->buf[Buf->BufUsed - 1] = '\0';
2861 while (a < Buf->BufUsed) {
2862 if (Buf->buf[a] == '+')
2864 else if (Buf->buf[a] == '%') {
2865 /* don't let % chars through, rather truncate the input. */
2866 if (a + 2 > Buf->BufUsed) {
2871 hex[0] = Buf->buf[a + 1];
2872 hex[1] = Buf->buf[a + 2];
2875 sscanf(hex, "%02x", &b);
2876 Buf->buf[a] = (char) b;
2877 len = Buf->BufUsed - a - 2;
2879 memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
2891 * RFC2047-encode a header field if necessary.
2892 * If no non-ASCII characters are found, the string
2893 * will be copied verbatim without encoding.
2895 * target Target buffer.
2896 * source Source string to be encoded.
2897 * @returns encoded length; -1 if non success.
2899 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
2901 const char headerStr[] = "=?UTF-8?Q?";
2902 int need_to_encode = 0;
2906 if ((source == NULL) ||
2910 while ((i < source->BufUsed) &&
2911 (!IsEmptyStr (&source->buf[i])) &&
2912 (need_to_encode == 0)) {
2913 if (((unsigned char) source->buf[i] < 32) ||
2914 ((unsigned char) source->buf[i] > 126)) {
2920 if (!need_to_encode) {
2921 if (*target == NULL) {
2922 *target = NewStrBufPlain(source->buf, source->BufUsed);
2925 FlushStrBuf(*target);
2926 StrBufAppendBuf(*target, source, 0);
2929 return (*target)->BufUsed;
2933 if (*target == NULL)
2934 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
2935 else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
2936 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
2937 memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
2938 (*target)->BufUsed = sizeof(headerStr) - 1;
2939 for (i=0; (i < source->BufUsed); ++i) {
2940 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2941 IncreaseBuf(*target, 1, 0);
2942 ch = (unsigned char) source->buf[i];
2951 sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
2952 (*target)->BufUsed += 3;
2956 (*target)->buf[(*target)->BufUsed] = '_';
2958 (*target)->buf[(*target)->BufUsed] = ch;
2959 (*target)->BufUsed++;
2963 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
2964 IncreaseBuf(*target, 1, 0);
2966 (*target)->buf[(*target)->BufUsed++] = '?';
2967 (*target)->buf[(*target)->BufUsed++] = '=';
2968 (*target)->buf[(*target)->BufUsed] = '\0';
2969 return (*target)->BufUsed;;
2972 // Quoted-Printable encode a message; make it < 80 columns width.
2973 // source Source string to be encoded.
2974 // returns buffer with encoded message.
2975 StrBuf *StrBufQuotedPrintableEncode(const StrBuf *EncodeMe) {
2978 const char *ptr, *eptr;
2982 OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
2984 OEptr = OutBuf->buf + OutBuf->BufSize;
2985 ptr = EncodeMe->buf;
2986 eptr = EncodeMe->buf + EncodeMe->BufUsed;
2989 while (ptr < eptr) {
2990 if (Optr + 4 >= OEptr) {
2992 Offset = Optr - OutBuf->buf;
2993 OutBuf->BufUsed = Optr - OutBuf->buf;
2994 IncreaseBuf(OutBuf, 1, 0);
2995 Optr = OutBuf->buf + Offset;
2996 OEptr = OutBuf->buf + OutBuf->BufSize;
2998 if (*ptr == '\r') { // ignore carriage returns
3001 else if (*ptr == '\n') { // hard line break
3002 memcpy(Optr, HKEY("=0A"));
3007 else if (( (*ptr >= 32) && (*ptr <= 60) ) || ( (*ptr >= 62) && (*ptr <= 126) )) {
3017 *Optr = HexList[ch][0];
3019 *Optr = HexList[ch][1];
3025 if (LinePos > 72) { // soft line break
3026 if (isspace(*(Optr - 1))) {
3031 *Optr = HexList[ch][0];
3033 *Optr = HexList[ch][1];
3045 OutBuf->BufUsed = Optr - OutBuf->buf;
3051 static void AddRecipient(StrBuf *Target, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3054 if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3055 if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3057 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\""), 0);
3058 StrBufRFC2047encode(&EncBuf, UserName);
3059 StrBufAppendBuf(Target, EncBuf, 0);
3060 if (QuoteMe) StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3061 else StrBufAppendBufPlain(Target, HKEY(" "), 0);
3063 if (StrLength(EmailAddress) > 0){
3064 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3065 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3066 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3072 * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3073 * \param Recp Source list of email recipients
3074 * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3075 * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3076 * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3077 * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3079 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, StrBuf *UserName, StrBuf *EmailAddress, StrBuf *EncBuf) {
3081 const char *pch, *pche;
3082 const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3084 if ((Recp == NULL) || (StrLength(Recp) == 0))
3088 pche = pch + StrLength(Recp);
3090 if (!CheckEncode(pch, -1, pche))
3091 return NewStrBufDup(Recp);
3093 Target = NewStrBufPlain(NULL, StrLength(Recp));
3095 while ((pch != NULL) && (pch < pche))
3097 while (isspace(*pch)) pch++;
3098 UserEnd = EmailStart = EmailEnd = NULL;
3100 if ((*pch == '"') || (*pch == '\'')) {
3101 UserStart = pch + 1;
3103 UserEnd = strchr(UserStart, *pch);
3104 if (UserEnd == NULL)
3105 break; ///TODO: Userfeedback??
3106 EmailStart = UserEnd + 1;
3107 while (isspace(*EmailStart))
3109 if (UserEnd == UserStart) {
3110 UserStart = UserEnd = NULL;
3113 if (*EmailStart == '<') {
3115 EmailEnd = strchr(EmailStart, '>');
3116 if (EmailEnd == NULL)
3117 EmailEnd = strchr(EmailStart, ',');
3121 EmailEnd = strchr(EmailStart, ',');
3123 if (EmailEnd == NULL)
3130 EmailEnd = strchr(UserStart, ',');
3131 if (EmailEnd == NULL) {
3132 EmailEnd = strchr(pch, '>');
3134 if (EmailEnd != NULL) {
3144 while ((EmailEnd > UserStart) && !gt &&
3145 ((*EmailEnd == ',') ||
3146 (*EmailEnd == '>') ||
3147 (isspace(*EmailEnd))))
3149 if (*EmailEnd == '>')
3154 if (EmailEnd == UserStart)
3158 EmailStart = strchr(UserStart, '<');
3159 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3161 UserEnd = EmailStart;
3163 while ((UserEnd > UserStart) &&
3164 isspace (*(UserEnd - 1)))
3167 if (UserStart >= UserEnd)
3168 UserStart = UserEnd = NULL;
3170 else { /* this is a local recipient... no domain, just a realname */
3171 EmailStart = UserStart;
3172 At = strchr(EmailStart, '@');
3178 EmailStart = UserStart;
3184 if ((UserStart != NULL) && (UserEnd != NULL))
3185 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3186 else if ((UserStart != NULL) && (UserEnd == NULL))
3187 StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3189 FlushStrBuf(UserName);
3191 if ((EmailStart != NULL) && (EmailEnd != NULL))
3192 StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3193 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3194 StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3196 FlushStrBuf(EmailAddress);
3198 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3203 if ((pch != NULL) && (*pch == ','))
3205 if (pch != NULL) while (isspace(*pch))
3213 * replaces all occurances of 'search' by 'replace'
3214 * buf Buffer to modify
3215 * search character to search
3216 * replace character to replace search by
3218 void StrBufReplaceChars(StrBuf *buf, char search, char replace) {
3222 for (i=0; i<buf->BufUsed; i++)
3223 if (buf->buf[i] == search)
3224 buf->buf[i] = replace;
3229 * removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3230 * buf Buffer to modify
3232 void StrBufToUnixLF(StrBuf *buf)
3234 char *pche, *pchS, *pchT;
3238 pche = buf->buf + buf->BufUsed;
3239 pchS = pchT = buf->buf;
3245 if (*pchS != '\n') {
3254 buf->BufUsed = pchT - buf->buf;
3258 /*******************************************************************************
3259 * Iconv Wrapper; RFC822 de/encoding *
3260 *******************************************************************************/
3263 * Wrapper around iconv_open()
3264 * Our version adds aliases for non-standard Microsoft charsets
3265 * such as 'MS950', aliasing them to names like 'CP950'
3267 * tocode Target encoding
3268 * fromcode Source encoding
3269 * pic anonimized pointer to iconv struct
3271 void ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3274 iconv_t ic = (iconv_t)(-1) ;
3275 ic = iconv_open(tocode, fromcode);
3276 if (ic == (iconv_t)(-1) ) {
3277 char alias_fromcode[64];
3278 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3279 safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3280 alias_fromcode[0] = 'C';
3281 alias_fromcode[1] = 'P';
3282 ic = iconv_open(tocode, alias_fromcode);
3285 *(iconv_t *)pic = ic;
3291 * find one chunk of a RFC822 encoded string
3292 * Buffer where to search
3293 * bptr where to start searching
3294 * @returns found position, NULL if none.
3296 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3299 /* Find the next ?Q? */
3300 if (Buf->BufUsed - (bptr - Buf->buf) < 6)
3303 end = strchr(bptr + 2, '?');
3308 if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3309 (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3310 ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) &&
3311 (*(end + 2) == '?')) {
3312 /* skip on to the end of the cluster, the next ?= */
3313 end = strstr(end + 3, "?=");
3316 /* sort of half valid encoding, try to find an end. */
3317 end = strstr(bptr, "?=");
3324 * convert one buffer according to the preselected iconv pointer PIC
3325 * ConvertBuf buffer we need to translate
3326 * TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3327 * pic Pointer to the iconv-session Object
3329 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3335 char *ibuf; /**< Buffer of characters to be converted */
3336 char *obuf; /**< Buffer for converted characters */
3337 size_t ibuflen; /**< Length of input buffer */
3338 size_t obuflen; /**< Length of output buffer */
3341 if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3344 /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3345 if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3346 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3348 ic = *(iconv_t*)pic;
3349 ibuf = ConvertBuf->buf;
3350 ibuflen = ConvertBuf->BufUsed;
3352 obuflen = TmpBuf->BufSize;
3354 siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3357 if (errno == E2BIG) {
3359 IncreaseBuf(TmpBuf, 0, 0);
3364 else if (errno == EILSEQ){
3365 /* hm, invalid utf8 sequence... what to do now? */
3366 /* An invalid multibyte sequence has been encountered in the input */
3368 else if (errno == EINVAL) {
3369 /* An incomplete multibyte sequence has been encountered in the input. */
3372 FlushStrBuf(TmpBuf);
3375 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3376 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3378 /* little card game: wheres the red lady? */
3379 iSwapBuffers(ConvertBuf, TmpBuf);
3380 FlushStrBuf(TmpBuf);
3387 * catches one RFC822 encoded segment, and decodes it.
3388 * Target buffer to fill with result
3389 * DecodeMe buffer with stuff to process
3390 * SegmentStart points to our current segment in DecodeMe
3391 * SegmentEnd Points to the end of our current segment in DecodeMe
3392 * ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3393 * ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3394 * FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3396 inline static void DecodeSegment(StrBuf *Target,
3397 const StrBuf *DecodeMe,
3398 const char *SegmentStart,
3399 const char *SegmentEnd,
3401 StrBuf *ConvertBuf2,
3402 StrBuf *FoundCharset)
3408 iconv_t ic = (iconv_t)(-1);
3412 /* Now we handle foreign character sets properly encoded
3413 * in RFC2047 format.
3415 StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3416 StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3417 StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3418 extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3419 if (FoundCharset != NULL) {
3420 FlushStrBuf(FoundCharset);
3421 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3423 extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3424 StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3426 *encoding = toupper(*encoding);
3427 if (*encoding == 'B') { /**< base64 */
3428 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3429 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3430 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf,
3432 ConvertBuf->BufUsed);
3434 else if (*encoding == 'Q') { /**< quoted-printable */
3438 while (pos < ConvertBuf->BufUsed)
3440 if (ConvertBuf->buf[pos] == '_')
3441 ConvertBuf->buf[pos] = ' ';
3445 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3446 IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3448 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3451 ConvertBuf->BufUsed);
3454 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3457 ctdl_iconv_open("UTF-8", charset, &ic);
3458 if (ic != (iconv_t)(-1) ) {
3460 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3461 StrBufAppendBuf(Target, ConvertBuf2, 0);
3466 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3472 * Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3473 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3474 * Target where to put the decoded string to
3475 * DecodeMe buffer with encoded string
3476 * DefaultCharset if we don't find one, which should we use?
3477 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3478 * put it here for later use where no string might be known.
3480 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3483 StrBuf *ConvertBuf2;
3484 ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3485 ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3487 StrBuf_RFC822_2_Utf8(Target,
3493 FreeStrBuf(&ConvertBuf);
3494 FreeStrBuf(&ConvertBuf2);
3498 * Handle subjects with RFC2047 encoding such as:
3499 * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3500 * Target where to put the decoded string to
3501 * DecodeMe buffer with encoded string
3502 * DefaultCharset if we don't find one, which should we use?
3503 * FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string,
3504 * put it here for later use where no string might be known.
3505 * ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3506 * ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3508 void StrBuf_RFC822_2_Utf8(StrBuf *Target,
3509 const StrBuf *DecodeMe,
3510 const StrBuf* DefaultCharset,
3511 StrBuf *FoundCharset,
3513 StrBuf *ConvertBuf2)
3515 StrBuf *DecodedInvalidBuf = NULL;
3516 const StrBuf *DecodeMee = DecodeMe;
3517 const char *start, *end, *next, *nextend, *ptr = NULL;
3519 iconv_t ic = (iconv_t)(-1) ;
3524 int illegal_non_rfc2047_encoding = 0;
3527 if (DecodeMe == NULL)
3529 /* Sometimes, badly formed messages contain strings which were simply
3530 * written out directly in some foreign character set instead of
3531 * using RFC2047 encoding. This is illegal but we will attempt to
3532 * handle it anyway by converting from a user-specified default
3533 * charset to UTF-8 if we see any nonprintable characters.
3536 for (i=0; i<DecodeMe->BufUsed; ++i) {
3537 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3538 illegal_non_rfc2047_encoding = 1;
3543 if ((illegal_non_rfc2047_encoding) &&
3544 (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) &&
3545 (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3548 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3549 if (ic != (iconv_t)(-1) ) {
3550 DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3551 StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3552 DecodeMee = DecodedInvalidBuf;
3558 /* pre evaluate the first pair */
3560 start = strstr(DecodeMee->buf, "=?");
3561 eptr = DecodeMee->buf + DecodeMee->BufUsed;
3563 end = FindNextEnd (DecodeMee, start + 2);
3565 StrBufAppendBuf(Target, DecodeMee, 0);
3566 FreeStrBuf(&DecodedInvalidBuf);
3571 if (start != DecodeMee->buf) {
3574 nFront = start - DecodeMee->buf;
3575 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3578 * Since spammers will go to all sorts of absurd lengths to get their
3579 * messages through, there are LOTS of corrupt headers out there.
3580 * So, prevent a really badly formed RFC2047 header from throwing
3581 * this function into an infinite loop.
3583 while ((start != NULL) &&
3590 DecodeSegment(Target,
3598 next = strstr(end, "=?");
3600 if ((next != NULL) &&
3602 nextend = FindNextEnd(DecodeMee, next);
3603 if (nextend == NULL)
3606 /* did we find two partitions */
3607 if ((next != NULL) &&
3611 while ((ptr < next) &&
3618 * did we find a gab just filled with blanks?
3619 * if not, copy its stuff over.
3623 StrBufAppendBufPlain(Target,
3629 /* our next-pair is our new first pair now. */
3635 nextend = DecodeMee->buf + DecodeMee->BufUsed;
3636 if ((end != NULL) && (end < nextend)) {
3638 while ( (ptr < nextend) &&
3645 StrBufAppendBufPlain(Target, end, nextend - end, 0);
3647 FreeStrBuf(&DecodedInvalidBuf);
3650 /*******************************************************************************
3651 * Manipulating UTF-8 Strings *
3652 *******************************************************************************/
3655 * evaluate the length of an utf8 special character sequence
3656 * Char the character to examine
3657 * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
3659 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
3662 unsigned char test = (1<<7);
3664 if ((*CharS & 0xC0) != 0xC0)
3668 ((test & ((unsigned char)*CharS)) != 0))
3673 if ((n > 6) || ((CharE - CharS) < n))
3679 * detect whether this char starts an utf-8 encoded char
3680 * Char character to inspect
3681 * @returns yes or no
3683 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
3685 /** 11??.???? indicates an UTF8 Sequence. */
3686 return ((Char & 0xC0) == 0xC0);
3690 * measure the number of glyphs in an UTF8 string...
3691 * Buf string to measure
3692 * @returns the number of glyphs in Buf
3694 long StrBuf_Utf8StrLen(StrBuf *Buf)
3700 if ((Buf == NULL) || (Buf->BufUsed == 0))
3703 eptr = Buf->buf + Buf->BufUsed;
3704 while ((aptr < eptr) && (*aptr != '\0')) {
3705 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3706 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3707 while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
3719 * cuts a string after maxlen glyphs
3720 * Buf string to cut to maxlen glyphs
3721 * maxlen how long may the string become?
3722 * @returns current length of the string
3724 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
3730 eptr = Buf->buf + Buf->BufUsed;
3731 while ((aptr < eptr) && (*aptr != '\0')) {
3732 if (Ctdl_IsUtf8SequenceStart(*aptr)){
3733 m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
3734 while ((*aptr++ != '\0') && (m-- > 0));
3743 Buf->BufUsed = aptr - Buf->buf;
3744 return Buf->BufUsed;
3747 return Buf->BufUsed;
3752 /*******************************************************************************
3754 *******************************************************************************/
3757 * uses the same calling syntax as compress2(), but it
3758 * creates a stream compatible with HTTP "Content-encoding: gzip"
3759 * dest compressed buffer
3760 * destLen length of the compresed data
3761 * source source to encode
3762 * sourceLen length of source to encode
3763 * level compression level
3766 int ZEXPORT compress_gzip(Bytef * dest,
3768 const Bytef * source,
3772 /* write gzip header */
3773 snprintf((char *) dest, *destLen,
3774 "%c%c%c%c%c%c%c%c%c%c",
3775 gz_magic[0], gz_magic[1], Z_DEFLATED,
3776 0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
3779 /* normal deflate */
3782 stream.next_in = (Bytef *) source;
3783 stream.avail_in = (uInt) sourceLen;
3784 stream.next_out = dest + 10L; // after header
3785 stream.avail_out = (uInt) * destLen;
3786 if ((uLong) stream.avail_out != *destLen)
3789 stream.zalloc = (alloc_func) 0;
3790 stream.zfree = (free_func) 0;
3791 stream.opaque = (voidpf) 0;
3793 err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
3794 DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
3798 err = deflate(&stream, Z_FINISH);
3799 if (err != Z_STREAM_END) {
3800 deflateEnd(&stream);
3801 return err == Z_OK ? Z_BUF_ERROR : err;
3803 *destLen = stream.total_out + 10L;
3805 /* write CRC and Length */
3806 uLong crc = crc32(0L, source, sourceLen);
3808 for (n = 0; n < 4; ++n, ++*destLen) {
3809 dest[*destLen] = (int) (crc & 0xff);
3812 uLong len = stream.total_in;
3813 for (n = 0; n < 4; ++n, ++*destLen) {
3814 dest[*destLen] = (int) (len & 0xff);
3817 err = deflateEnd(&stream);
3824 * compress the buffer with gzip
3825 * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
3826 * Buf buffer whose content is to be gzipped
3828 int CompressBuffer(StrBuf *Buf)
3831 char *compressed_data = NULL;
3832 size_t compressed_len, bufsize;
3835 bufsize = compressed_len = Buf->BufUsed + (Buf->BufUsed / 100) + 100;
3836 compressed_data = malloc(compressed_len);
3838 if (compressed_data == NULL)
3840 /* Flush some space after the used payload so valgrind shuts up... */
3841 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3842 Buf->buf[Buf->BufUsed + i++] = '\0';
3843 if (compress_gzip((Bytef *) compressed_data,
3846 (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
3849 Buf->buf = compressed_data;
3850 Buf->BufUsed = compressed_len;
3851 Buf->BufSize = bufsize;
3852 /* Flush some space after the used payload so valgrind shuts up... */
3854 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
3855 Buf->buf[Buf->BufUsed + i++] = '\0';
3858 free(compressed_data);
3860 #endif /* HAVE_ZLIB */
3865 /*******************************************************************************
3866 * File I/O; Callbacks to libevent *
3867 *******************************************************************************/
3869 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
3874 if ((FB == NULL) || (FB->Buf == NULL))
3878 * check whether the read pointer is somewhere in a range
3879 * where a cut left is inexpensive
3882 if (FB->ReadWritePointer != NULL) {
3885 already_read = FB->ReadWritePointer - FB->Buf->buf;
3886 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3888 if (already_read != 0) {
3891 unread = FB->Buf->BufUsed - already_read;
3893 /* else nothing to compact... */
3895 FB->ReadWritePointer = FB->Buf->buf;
3896 bufremain = FB->Buf->BufSize;
3898 else if ((unread < 64) || (bufremain < already_read)) {
3900 * if its just a tiny bit remaining, or we run out of space...
3903 FB->Buf->BufUsed = unread;
3904 if (unread < already_read)
3905 memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
3907 memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
3908 FB->ReadWritePointer = FB->Buf->buf;
3909 bufremain = FB->Buf->BufSize - unread - 1;
3911 else if (bufremain < (FB->Buf->BufSize / 10)) {
3912 /* get a bigger buffer */
3914 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
3916 FB->ReadWritePointer = FB->Buf->buf + unread;
3918 bufremain = FB->Buf->BufSize - unread - 1;
3919 /*TODO: special increase function that won't copy the already read! */
3922 else if (bufremain < 10) {
3923 IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
3925 FB->ReadWritePointer = FB->Buf->buf;
3927 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
3932 FB->ReadWritePointer = FB->Buf->buf;
3933 bufremain = FB->Buf->BufSize - 1;
3936 n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
3939 FB->Buf->BufUsed += n;
3940 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
3946 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB) {
3950 if ((FB == NULL) || (FB->Buf == NULL))
3953 if (FB->ReadWritePointer != NULL)
3955 WriteRemain = FB->Buf->BufUsed -
3956 (FB->ReadWritePointer -
3960 FB->ReadWritePointer = FB->Buf->buf;
3961 WriteRemain = FB->Buf->BufUsed;
3964 n = write(fd, FB->ReadWritePointer, WriteRemain);
3966 FB->ReadWritePointer += n;
3968 if (FB->ReadWritePointer ==
3969 FB->Buf->buf + FB->Buf->BufUsed)
3971 FlushStrBuf(FB->Buf);
3972 FB->ReadWritePointer = NULL;
3975 // check whether we've got something to write
3976 // get the maximum chunk plus the pointer we can send
3977 // write whats there
3978 // if not all was sent, remember the send pointer for the next time
3979 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
3985 // extract a "next line" from Buf; Ptr to persist across several iterations
3986 // LineBuf your line will be copied here.
3987 // FB BLOB with lines of text...
3988 // Ptr moved arround to keep the next-line across several iterations
3989 // has to be &NULL on start; will be &NotNULL on end of buffer
3990 // returns size of copied buffer
3991 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB) {
3992 const char *aptr, *ptr, *eptr;
3995 if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
3999 if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4000 FB->ReadWritePointer = StrBufNOTNULL;
4004 FlushStrBuf(LineBuf);
4005 if (FB->ReadWritePointer == NULL)
4006 ptr = aptr = FB->Buf->buf;
4008 ptr = aptr = FB->ReadWritePointer;
4010 optr = LineBuf->buf;
4011 eptr = FB->Buf->buf + FB->Buf->BufUsed;
4012 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4014 while ((ptr <= eptr) && (*ptr != '\n') && (*ptr != '\r') ) {
4018 LineBuf->BufUsed = optr - LineBuf->buf;
4019 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4020 optr = LineBuf->buf + LineBuf->BufUsed;
4021 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4026 if (optr > LineBuf->buf)
4028 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4029 LineBuf->BufUsed = optr - LineBuf->buf;
4031 if ((FB->ReadWritePointer != NULL) && (FB->ReadWritePointer != FB->Buf->buf)) {
4032 // Ok, the client application read all the data
4033 // it was interested in so far. Since there is more to read,
4034 // we now shrink the buffer, and move the rest over.
4035 StrBufCutLeft(FB->Buf, FB->ReadWritePointer - FB->Buf->buf);
4036 FB->ReadWritePointer = FB->Buf->buf;
4038 return eMustReadMore;
4041 LineBuf->BufUsed = optr - LineBuf->buf;
4043 if ((ptr <= eptr) && (*ptr == '\r'))
4045 if ((ptr <= eptr) && (*ptr == '\n'))
4049 FB->ReadWritePointer = ptr;
4052 FlushStrBuf(FB->Buf);
4053 FB->ReadWritePointer = NULL;
4056 return eReadSuccess;
4060 * check whether the chunk-buffer has more data waiting or not.
4061 * FB Chunk-Buffer to inspect
4063 eReadState StrBufCheckBuffer(IOBuffer *FB)
4067 if (FB->Buf->BufUsed == 0)
4068 return eReadSuccess;
4069 if (FB->ReadWritePointer == NULL)
4070 return eBufferNotEmpty;
4071 if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4072 return eBufferNotEmpty;
4073 return eReadSuccess;
4077 long IOBufferStrLength(IOBuffer *FB) {
4078 if ((FB == NULL) || (FB->Buf == NULL))
4080 if (FB->ReadWritePointer == NULL)
4081 return StrLength(FB->Buf);
4083 return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4087 /*******************************************************************************
4088 * File I/O; Prefer buffered read since its faster! *
4089 *******************************************************************************/
4092 * Read a line from socket
4093 * flushes and closes the FD on error
4094 * buf the buffer to get the input to
4095 * fd pointer to the filedescriptor to read
4096 * append Append to an existing string or replace?
4097 * Error strerror() on error
4098 * @returns numbers of chars read
4100 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4102 int len, rlen, slen;
4104 if ((buf == NULL) || (buf->buf == NULL)) {
4105 *Error = strerror(EINVAL);
4112 slen = len = buf->BufUsed;
4114 rlen = read(*fd, &buf->buf[len], 1);
4116 *Error = strerror(errno);
4123 if (buf->buf[len] == '\n')
4125 if (buf->buf[len] != '\r')
4127 if (len + 2 >= buf->BufSize) {
4129 buf->buf[len+1] = '\0';
4130 IncreaseBuf(buf, 1, -1);
4134 buf->buf[len] = '\0';
4139 // Read a line from socket
4140 // flushes and closes the FD on error
4141 // Line the line to read from the fd / I/O Buffer
4142 // buf the buffer to get the input to
4143 // fd pointer to the filedescriptor to read
4144 // timeout number of successless selects until we bail out
4145 // selectresolution how long to wait on each select
4146 // Error strerror() on error
4147 // returns numbers of chars read
4148 int StrBufTCP_read_buffered_line(StrBuf *Line,
4152 int selectresolution,
4156 int nSuccessLess = 0;
4163 if (buf->BufUsed > 0) {
4164 pch = strchr(buf->buf, '\n');
4167 len = pch - buf->buf;
4168 if (len > 0 && (*(pch - 1) == '\r') ) {
4171 StrBufSub(Line, buf, 0, len - rlen);
4172 StrBufCutLeft(buf, len + 1);
4177 if (buf->BufSize - buf->BufUsed < 10) {
4178 IncreaseBuf(buf, 1, -1);
4181 fdflags = fcntl(*fd, F_GETFL);
4182 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4184 while ((nSuccessLess < timeout) && (pch == NULL)) {
4186 tv.tv_sec = selectresolution;
4191 if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4192 *Error = strerror(errno);
4198 if (IsNonBlock && ! FD_ISSET(*fd, &rfds)) {
4202 rlen = read(*fd, &buf->buf[buf->BufUsed], buf->BufSize - buf->BufUsed - 1);
4204 *Error = strerror(errno);
4209 else if (rlen > 0) {
4211 buf->BufUsed += rlen;
4212 buf->buf[buf->BufUsed] = '\0';
4213 pch = strchr(buf->buf, '\n');
4214 if ((pch == NULL) && (buf->BufUsed + 10 > buf->BufSize) && (IncreaseBuf(buf, 1, -1) == -1)) {
4223 len = pch - buf->buf;
4224 if (len > 0 && (*(pch - 1) == '\r') )
4226 StrBufSub(Line, buf, 0, len - rlen);
4227 StrBufCutLeft(buf, len + 1);
4234 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4235 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4236 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4239 // Read a line from socket
4240 // flushes and closes the FD on error
4241 // Line where to append our Line read from the fd / I/O Buffer;
4242 // IOBuf the buffer to get the input to; lifetime pair to FD
4243 // Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4244 // fd pointer to the filedescriptor to read
4245 // timeout number of successless selects until we bail out
4246 // selectresolution how long to wait on each select
4247 // Error strerror() on error
4248 // returns numbers of chars read or -1 in case of error. "\n" will become 0
4249 int StrBufTCP_read_buffered_line_fast(StrBuf *Line,
4254 int selectresolution,
4257 const char *pche = NULL;
4258 const char *pos = NULL;
4260 int len, rlen, retlen;
4261 int nSuccessLess = 0;
4263 const char *pch = NULL;
4269 if ((Line == NULL) || (Pos == NULL) || (IOBuf == NULL) || (*fd == -1)) {
4273 *Error = ErrRBLF_PreConditionFailed;
4278 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed)) {
4281 pche = IOBuf->buf + IOBuf->BufUsed;
4285 while ((pch < pche) && (*pch != '\n')) {
4286 if (Line->BufUsed + 10 > Line->BufSize) {
4288 apos = pcht - Line->buf;
4290 IncreaseBuf(Line, 1, -1);
4291 pcht = Line->buf + apos;
4299 if (len > 0 && (*(pch - 1) == '\r') ) {
4307 if ((pch >= pche) || (*pch == '\0')) {
4314 if ((pch != NULL) && (pch <= pche)) {
4315 if (pch + 1 >= pche) {
4328 /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4330 if (IOBuf->BufSize - IOBuf->BufUsed < 10) {
4331 IncreaseBuf(IOBuf, 1, -1);
4334 fdflags = fcntl(*fd, F_GETFL);
4335 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4338 while ((nSuccessLess < timeout) &&
4347 if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4348 *Error = strerror(errno);
4352 *Error = ErrRBLF_SelectFailed;
4355 if (! FD_ISSET(*fd, &rfds) != 0) {
4360 rlen = read(*fd, &IOBuf->buf[IOBuf->BufUsed], IOBuf->BufSize - IOBuf->BufUsed - 1);
4362 *Error = strerror(errno);
4367 else if (rlen > 0) {
4369 pLF = IOBuf->buf + IOBuf->BufUsed;
4370 IOBuf->BufUsed += rlen;
4371 IOBuf->buf[IOBuf->BufUsed] = '\0';
4373 pche = IOBuf->buf + IOBuf->BufUsed;
4375 while ((pLF < pche) && (*pLF != '\n'))
4377 if ((pLF >= pche) || (*pLF == '\0'))
4380 if (IOBuf->BufUsed + 10 > IOBuf->BufSize) {
4383 if (pLF != NULL) apos = pLF - IOBuf->buf;
4384 IncreaseBuf(IOBuf, 1, -1);
4385 if (pLF != NULL) pLF = IOBuf->buf + apos;
4398 if (len > 0 && (*(pLF - 1) == '\r') ) {
4401 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4402 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed) {
4407 return retlen + len;
4409 *Error = ErrRBLF_NotEnoughSentFromServer;
4414 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4416 * Input binary data from socket
4417 * flushes and closes the FD on error
4418 * Buf the buffer to get the input to
4419 * fd pointer to the filedescriptor to read
4420 * append Append to an existing string or replace?
4421 * nBytes the maximal number of bytes to read
4422 * Error strerror() on error
4423 * @returns numbers of chars read
4425 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4436 if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1)) {
4437 *Error = ErrRBLF_BLOBPreConditionFailed;
4443 if (Buf->BufUsed + nBytes >= Buf->BufSize) {
4444 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4446 ptr = Buf->buf + Buf->BufUsed;
4448 fdflags = fcntl(*fd, F_GETFL);
4449 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4451 while ((nRead < nBytes) && (*fd != -1)) {
4458 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4459 *Error = strerror(errno);
4462 if (*Error == NULL) {
4463 *Error = ErrRBLF_SelectFailed;
4467 if (! FD_ISSET(*fd, &rfds) != 0) {
4473 if ((rlen = read(*fd,
4475 nBytes - nRead)) == -1) {
4478 *Error = strerror(errno);
4483 Buf->BufUsed += rlen;
4485 Buf->buf[Buf->BufUsed] = '\0';
4489 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4490 const char *ErrRBB_too_many_selects = "StrBufReadBLOBBuffered: to many selects; aborting.";
4493 // Input binary data from socket
4494 // flushes and closes the FD on error
4495 // Blob put binary thing here
4496 // IOBuf the buffer to get the input to
4497 // Pos offset inside of IOBuf
4498 // fd pointer to the filedescriptor to read
4499 // append Append to an existing string or replace?
4500 // nBytes the maximal number of bytes to read
4501 // check whether we should search for '000\n' terminators in case of timeouts
4502 // Error strerror() on error
4503 // returns numbers of chars read
4504 int StrBufReadBLOBBuffered(StrBuf *Blob,
4517 int nAlreadyRead = 0;
4522 int nSuccessLess = 0;
4525 if ((Blob == NULL) || (*fd == -1) || (IOBuf == NULL) || (Pos == NULL)) {
4528 *Error = ErrRBB_BLOBFPreConditionFailed;
4534 if (Blob->BufUsed + nBytes >= Blob->BufSize)
4535 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4540 rlen = pos - IOBuf->buf;
4542 rlen = IOBuf->BufUsed - rlen;
4545 if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed)) {
4546 if (rlen < nBytes) {
4547 memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4548 Blob->BufUsed += rlen;
4549 Blob->buf[Blob->BufUsed] = '\0';
4550 nAlreadyRead = nRead = rlen;
4553 if (rlen >= nBytes) {
4554 memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4555 Blob->BufUsed += nBytes;
4556 Blob->buf[Blob->BufUsed] = '\0';
4557 if (rlen == nBytes) {
4569 if (IOBuf->BufSize < nBytes - nRead) {
4570 IncreaseBuf(IOBuf, 0, nBytes - nRead);
4574 fdflags = fcntl(*fd, F_GETFL);
4575 IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4583 while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
4590 if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4591 *Error = strerror(errno);
4594 if (*Error == NULL) {
4595 *Error = ErrRBLF_SelectFailed;
4599 if (! FD_ISSET(*fd, &rfds) != 0) {
4604 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
4605 if (rlen < 1) { // We will always get at least 1 byte unless the connection is broken
4608 *Error = strerror(errno);
4611 else if (rlen == 0){
4612 if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
4613 StrBufPlain(Blob, HKEY("\n000\n"));
4614 StrBufCutRight(Blob, 5);
4615 return Blob->BufUsed;
4617 else if (!IsNonBlock)
4619 else if (nSuccessLess > MaxTries) {
4621 *Error = ErrRBB_too_many_selects;
4625 else if (rlen > 0) {
4629 IOBuf->BufUsed += rlen;
4632 if (nSuccessLess >= MaxTries) {
4634 *Error = ErrRBB_too_many_selects;
4638 if (nRead > nBytes) {
4639 *Pos = IOBuf->buf + nBytes;
4641 Blob->buf[Blob->BufUsed] = '\0';
4642 StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
4646 return nRead + nAlreadyRead;
4650 // extract a "next line" from Buf; Ptr to persist across several iterations
4651 // LineBuf your line will be copied here.
4652 // Buf BLOB with lines of text...
4653 // Ptr moved arround to keep the next-line across several iterations
4654 // has to be &NULL on start; will be &NotNULL on end of buffer
4655 // returns size of remaining buffer
4656 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr) {
4657 const char *aptr, *ptr, *eptr;
4660 if ((Buf == NULL) || (*Ptr == StrBufNOTNULL) || (LineBuf == NULL)|| (LineBuf->buf == NULL)) {
4661 *Ptr = StrBufNOTNULL;
4665 FlushStrBuf(LineBuf);
4667 ptr = aptr = Buf->buf;
4671 optr = LineBuf->buf;
4672 eptr = Buf->buf + Buf->BufUsed;
4673 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4675 while ((ptr <= eptr) && (*ptr != '\n') && (*ptr != '\r') ) {
4679 LineBuf->BufUsed = optr - LineBuf->buf;
4680 IncreaseBuf(LineBuf, 1, LineBuf->BufUsed + 1);
4681 optr = LineBuf->buf + LineBuf->BufUsed;
4682 xptr = LineBuf->buf + LineBuf->BufSize - 1;
4686 if ((ptr >= eptr) && (optr > LineBuf->buf))
4688 LineBuf->BufUsed = optr - LineBuf->buf;
4690 if ((ptr <= eptr) && (*ptr == '\r'))
4692 if ((ptr <= eptr) && (*ptr == '\n'))
4699 *Ptr = StrBufNOTNULL;
4702 return Buf->BufUsed - (ptr - Buf->buf);
4706 // removes double slashes from pathnames
4707 // Dir directory string to filter
4708 // RemoveTrailingSlash allows / disallows trailing slashes
4709 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash) {
4714 while (!IsEmptyStr(a)) {
4726 if ((RemoveTrailingSlash) &&
4732 Dir->BufUsed = b - Dir->buf;
4736 // Decode a quoted-printable encoded StrBuf buffer "in place"
4737 // This is possible because the decoded will always be shorter than the encoded
4738 // so we don't have to worry about the buffer being to small.
4739 void StrBufDecodeQP(StrBuf *Buf) {
4740 if (!Buf) { // sanity check #1
4744 int source_len = StrLength(Buf);
4745 if (source_len < 1) { // sanity check #2
4749 int spos = 0; // source position
4750 int tpos = 0; // target position
4752 while (spos < source_len) {
4753 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
4756 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
4759 else if (Buf->buf[spos] == '=') {
4762 sscanf(&Buf->buf[spos], "%02x", &ch);
4763 Buf->buf[tpos++] = ch;
4767 Buf->buf[tpos++] = Buf->buf[spos++];
4772 Buf->BufUsed = tpos;