Removed traces after realizing that I was hitting config.c_maxmsglen and not an actua...
[citadel.git] / libcitadel / lib / stringbuf.c
1 // Copyright (c) 1987-2022 by the citadel.org team
2 //
3 // This program is open source software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 3 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
17 #define _GNU_SOURCE
18 #include "sysdep.h"
19 #include <ctype.h>
20 #include <errno.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <stdio.h>
25 #include <sys/select.h>
26 #include <fcntl.h>
27 #include <sys/types.h>
28 #define SHOW_ME_VAPPEND_PRINTF
29 #include <stdarg.h>
30
31 #include "libcitadel.h"
32
33 #include "b64/cencode.h"
34 #include "b64/cdecode.h"
35
36 #ifdef HAVE_ICONV
37 #include <iconv.h>
38 #endif
39
40 #ifdef HAVE_BACKTRACE
41 #include <execinfo.h>
42 #endif
43
44 #ifdef UNDEF_MEMCPY
45 #undef memcpy
46 #endif
47
48 #ifdef HAVE_ZLIB
49 #include <zlib.h>
50 int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
51                           const Bytef * source, uLong sourceLen, int level);
52 #endif
53 int BaseStrBufSize = 64;
54 int EnableSplice = 0;
55 int ZLibCompressionRatio = -1; /* defaults to 6 */
56 #ifdef HAVE_ZLIB
57 #define DEF_MEM_LEVEL 8 /*< memlevel??? */
58 #define OS_CODE 0x03    /*< unix */
59 const int gz_magic[2] = { 0x1f, 0x8b }; /* gzip magic header */
60 #endif
61
62 const char *StrBufNOTNULL = ((char*) NULL) - 1;
63
64 const char HexList[256][3] = {
65         "00","01","02","03","04","05","06","07","08","09","0A","0B","0C","0D","0E","0F",
66         "10","11","12","13","14","15","16","17","18","19","1A","1B","1C","1D","1E","1F",
67         "20","21","22","23","24","25","26","27","28","29","2A","2B","2C","2D","2E","2F",
68         "30","31","32","33","34","35","36","37","38","39","3A","3B","3C","3D","3E","3F",
69         "40","41","42","43","44","45","46","47","48","49","4A","4B","4C","4D","4E","4F",
70         "50","51","52","53","54","55","56","57","58","59","5A","5B","5C","5D","5E","5F",
71         "60","61","62","63","64","65","66","67","68","69","6A","6B","6C","6D","6E","6F",
72         "70","71","72","73","74","75","76","77","78","79","7A","7B","7C","7D","7E","7F",
73         "80","81","82","83","84","85","86","87","88","89","8A","8B","8C","8D","8E","8F",
74         "90","91","92","93","94","95","96","97","98","99","9A","9B","9C","9D","9E","9F",
75         "A0","A1","A2","A3","A4","A5","A6","A7","A8","A9","AA","AB","AC","AD","AE","AF",
76         "B0","B1","B2","B3","B4","B5","B6","B7","B8","B9","BA","BB","BC","BD","BE","BF",
77         "C0","C1","C2","C3","C4","C5","C6","C7","C8","C9","CA","CB","CC","CD","CE","CF",
78         "D0","D1","D2","D3","D4","D5","D6","D7","D8","D9","DA","DB","DC","DD","DE","DF",
79         "E0","E1","E2","E3","E4","E5","E6","E7","E8","E9","EA","EB","EC","ED","EE","EF",
80         "F0","F1","F2","F3","F4","F5","F6","F7","F8","F9","FA","FB","FC","FD","FE","FF"};
81
82 /**
83  * @defgroup StrBuf Stringbuffer, A class for manipulating strings with dynamic buffers
84  * StrBuf is a versatile class, aiding the handling of dynamic strings
85  *  * reduce de/reallocations
86  *  * reduce the need to remeasure it
87  *  * reduce scanning over the string (in @ref StrBuf_NextTokenizer "Tokenizers")
88  *  * allow asyncroneous IO for line and Blob based operations
89  *  * reduce the use of memove in those
90  *  * Quick filling in several operations with append functions
91  */
92
93 /**
94  * @defgroup StrBuf_DeConstructors Create/Destroy StrBufs
95  * @ingroup StrBuf
96  */
97
98 /**
99  * @defgroup StrBuf_Cast Cast operators to interact with char* based code
100  * @ingroup StrBuf
101  * use these operators to interfere with code demanding char*; 
102  * if you need to own the content, smash me. Avoid, since we loose the length information.
103  */
104
105 /**
106  * @defgroup StrBuf_Filler Create/Replace/Append Content into a StrBuf
107  * @ingroup StrBuf
108  * operations to get your Strings into a StrBuf, manipulating them, or appending
109  */
110 /**
111  * @defgroup StrBuf_NextTokenizer Fast tokenizer to pull tokens in sequence 
112  * @ingroup StrBuf
113  * Quick tokenizer; demands of the user to pull its tokens in sequence
114  */
115
116 /**
117  * @defgroup StrBuf_Tokenizer tokenizer Functions; Slow ones.
118  * @ingroup StrBuf
119  * versatile tokenizer; random access to tokens, but slower; Prefer the @ref StrBuf_NextTokenizer "Next Tokenizer"
120  */
121
122 /**
123  * @defgroup StrBuf_BufferedIO Buffered IO with Asynchroneous reads and no unneeded memmoves (the fast ones)
124  * @ingroup StrBuf
125  * File IO to fill StrBufs; Works with work-buffer shared across several calls;
126  * External Cursor to maintain the current read position inside of the buffer
127  * the non-fast ones will use memove to keep the start of the buffer the read buffer (which is slower) 
128  */
129
130 /**
131  * @defgroup StrBuf_IO FileIO; Prefer @ref StrBuf_BufferedIO
132  * @ingroup StrBuf
133  * Slow I/O; avoid.
134  */
135
136 /**
137  * @defgroup StrBuf_DeEnCoder functions to translate the contents of a buffer
138  * @ingroup StrBuf
139  * these functions translate the content of a buffer into another representation;
140  * some are combined Fillers and encoders
141  */
142
143 /**
144  * Private Structure for the Stringbuffer
145  */
146 struct StrBuf {
147         char *buf;         /**< the pointer to the dynamic buffer */
148         long BufSize;      /**< how many spcae do we optain */
149         long BufUsed;      /**< StNumber of Chars used excluding the trailing \\0 */
150         int ConstBuf;      /**< are we just a wrapper arround a static buffer and musn't we be changed? */
151 #ifdef SIZE_DEBUG
152         long nIncreases;   /**< for profiling; cound how many times we needed more */
153         char bt [SIZ];     /**< Stacktrace of last increase */
154         char bt_lastinc [SIZ]; /**< How much did we increase last time? */
155 #endif
156 };
157
158
159 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE);
160 static inline int Ctdl_IsUtf8SequenceStart(const char Char);
161
162 #ifdef SIZE_DEBUG
163 #ifdef HAVE_BACKTRACE
164 static void StrBufBacktrace(StrBuf *Buf, int which)
165 {
166         int n;
167         char *pstart, *pch;
168         void *stack_frames[50];
169         size_t size, i;
170         char **strings;
171
172         if (which)
173                 pstart = pch = Buf->bt;
174         else
175                 pstart = pch = Buf->bt_lastinc;
176         size = backtrace(stack_frames, sizeof(stack_frames) / sizeof(void*));
177         strings = backtrace_symbols(stack_frames, size);
178         for (i = 0; i < size; i++) {
179                 if (strings != NULL)
180                         n = snprintf(pch, SIZ - (pch - pstart), "%s\\n", strings[i]);
181                 else
182                         n = snprintf(pch, SIZ - (pch - pstart), "%p\\n", stack_frames[i]);
183                 pch += n;
184         }
185         free(strings);
186
187
188 }
189 #endif
190
191 void dbg_FreeStrBuf(StrBuf *FreeMe, char *FromWhere)
192 {
193         if (hFreeDbglog == -1){
194                 pid_t pid = getpid();
195                 char path [SIZ];
196                 snprintf(path, SIZ, "/tmp/libcitadel_strbuf_realloc.log.%d", pid);
197                 hFreeDbglog = open(path, O_APPEND|O_CREAT|O_WRONLY);
198         }
199         if ((*FreeMe)->nIncreases > 0)
200         {
201                 char buf[SIZ * 3];
202                 long n;
203                 n = snprintf(buf, SIZ * 3, "%c+|%ld|%ld|%ld|%s|%s|\n",
204                              FromWhere,
205                              (*FreeMe)->nIncreases,
206                              (*FreeMe)->BufUsed,
207                              (*FreeMe)->BufSize,
208                              (*FreeMe)->bt,
209                              (*FreeMe)->bt_lastinc);
210                 n = write(hFreeDbglog, buf, n);
211         }
212         else
213         {
214                 char buf[128];
215                 long n;
216                 n = snprintf(buf, 128, "%c_|0|%ld%ld|\n",
217                              FromWhere,
218                              (*FreeMe)->BufUsed,
219                              (*FreeMe)->BufSize);
220                 n = write(hFreeDbglog, buf, n);
221         }
222 }
223
224 void dbg_IncreaseBuf(StrBuf *IncMe)
225 {
226         Buf->nIncreases++;
227 #ifdef HAVE_BACKTRACE
228         StrBufBacktrace(Buf, 1);
229 #endif
230 }
231
232 void dbg_Init(StrBuf *Buf)
233 {
234         Buf->nIncreases = 0;
235         Buf->bt[0] = '\0';
236         Buf->bt_lastinc[0] = '\0';
237 #ifdef HAVE_BACKTRACE
238         StrBufBacktrace(Buf, 0);
239 #endif
240 }
241
242 #else
243 /* void it... */
244 #define dbg_FreeStrBuf(a, b)
245 #define dbg_IncreaseBuf(a)
246 #define dbg_Init(a)
247
248 #endif
249
250 /**
251  * @ingroup StrBuf
252  * @brief swaps the contents of two StrBufs
253  * this is to be used to have cheap switched between a work-buffer and a target buffer 
254  * @param A First one
255  * @param B second one
256  */
257 static inline void iSwapBuffers(StrBuf *A, StrBuf *B)
258 {
259         StrBuf C;
260
261         memcpy(&C, A, sizeof(*A));
262         memcpy(A, B, sizeof(*B));
263         memcpy(B, &C, sizeof(C));
264
265 }
266
267 void SwapBuffers(StrBuf *A, StrBuf *B)
268 {
269         iSwapBuffers(A, B);
270 }
271
272
273 /** 
274  * @ingroup StrBuf_Cast
275  * @brief Cast operator to Plain String 
276  * @note if the buffer is altered by StrBuf operations, this pointer may become 
277  *  invalid. So don't lean on it after altering the buffer!
278  *  Since this operation is considered cheap, rather call it often than risking
279  *  your pointer to become invalid!
280  * @param Str the string we want to get the c-string representation for
281  * @returns the Pointer to the Content. Don't mess with it!
282  */
283 inline const char *ChrPtr(const StrBuf *Str)
284 {
285         if (Str == NULL)
286                 return "";
287         return Str->buf;
288 }
289
290 /**
291  * @ingroup StrBuf_Cast
292  * @brief since we know strlen()'s result, provide it here.
293  * @param Str the string to return the length to
294  * @returns contentlength of the buffer
295  */
296 inline int StrLength(const StrBuf *Str)
297 {
298         return (Str != NULL) ? Str->BufUsed : 0;
299 }
300
301 // local utility function to resize the buffer
302 // Buf          the buffer whichs storage we should increase
303 // KeepOriginal should we copy the original buffer or just start over with a new one
304 // DestSize     what should fit in after?
305 static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize) {
306         char *NewBuf;
307         size_t NewSize = Buf->BufSize * 2;
308
309         if (Buf->ConstBuf) {
310                 return -1;
311         }
312                 
313         if (DestSize > 0) {
314                 while ((NewSize <= DestSize) && (NewSize != 0)) {
315                         NewSize *= 2;
316                 }
317         }
318
319         if (NewSize == 0) {
320                 return -1;
321         }
322
323         NewBuf = (char*) malloc(NewSize);
324         if (NewBuf == NULL) {
325                 return -1;
326         }
327
328         if (KeepOriginal && (Buf->BufUsed > 0)) {
329                 memcpy(NewBuf, Buf->buf, Buf->BufUsed);
330         }
331         else {
332                 NewBuf[0] = '\0';
333                 Buf->BufUsed = 0;
334         }
335         free (Buf->buf);
336         Buf->buf = NewBuf;
337         Buf->BufSize = NewSize;
338
339         dbg_IncreaseBuf(Buf);
340
341         return Buf->BufSize;
342 }
343
344
345 // shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
346 // Buf          Buffer to shrink (has to be empty)
347 // ThreshHold   if the buffer is bigger then this, its readjusted
348 // NewSize      if we Shrink it, how big are we going to be afterwards?
349 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize) {
350         if ((Buf != NULL) && (Buf->BufUsed == 0) && (Buf->BufSize < ThreshHold)) {
351                 free(Buf->buf);
352                 Buf->buf = (char*) malloc(NewSize);
353                 Buf->BufUsed = 0;
354                 Buf->BufSize = NewSize;
355         }
356 }
357
358
359 /**
360  * @ingroup StrBuf_DeConstructors
361  * @brief shrink long term buffers to their real size so they don't waste memory
362  * @param Buf buffer to shrink
363  * @param Force if not set, will just executed if the buffer is much to big; set for lifetime strings
364  * @returns physical size of the buffer
365  */
366 long StrBufShrinkToFit(StrBuf *Buf, int Force)
367 {
368         if (Buf == NULL)
369                 return -1;
370         if (Force || 
371             (Buf->BufUsed + (Buf->BufUsed / 3) > Buf->BufSize))
372         {
373                 char *TmpBuf;
374
375                 TmpBuf = (char*) malloc(Buf->BufUsed + 1);
376                 if (TmpBuf == NULL)
377                         return -1;
378
379                 memcpy (TmpBuf, Buf->buf, Buf->BufUsed + 1);
380                 Buf->BufSize = Buf->BufUsed + 1;
381                 free(Buf->buf);
382                 Buf->buf = TmpBuf;
383         }
384         return Buf->BufUsed;
385 }
386
387 /**
388  * @ingroup StrBuf_DeConstructors
389  * @brief Allocate a new buffer with default buffer size
390  * @returns the new stringbuffer
391  */
392 StrBuf* NewStrBuf(void)
393 {
394         StrBuf *NewBuf;
395
396         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
397         if (NewBuf == NULL)
398                 return NULL;
399
400         NewBuf->buf = (char*) malloc(BaseStrBufSize);
401         if (NewBuf->buf == NULL)
402         {
403                 free(NewBuf);
404                 return NULL;
405         }
406         NewBuf->buf[0] = '\0';
407         NewBuf->BufSize = BaseStrBufSize;
408         NewBuf->BufUsed = 0;
409         NewBuf->ConstBuf = 0;
410
411         dbg_Init (NewBuf);
412
413         return NewBuf;
414 }
415
416 /** 
417  * @ingroup StrBuf_DeConstructors
418  * @brief Copy Constructor; returns a duplicate of CopyMe
419  * @param CopyMe Buffer to faxmilate
420  * @returns the new stringbuffer
421  */
422 StrBuf* NewStrBufDup(const StrBuf *CopyMe)
423 {
424         StrBuf *NewBuf;
425         
426         if (CopyMe == NULL)
427                 return NewStrBuf();
428
429         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
430         if (NewBuf == NULL)
431                 return NULL;
432
433         NewBuf->buf = (char*) malloc(CopyMe->BufSize);
434         if (NewBuf->buf == NULL)
435         {
436                 free(NewBuf);
437                 return NULL;
438         }
439
440         memcpy(NewBuf->buf, CopyMe->buf, CopyMe->BufUsed + 1);
441         NewBuf->BufUsed = CopyMe->BufUsed;
442         NewBuf->BufSize = CopyMe->BufSize;
443         NewBuf->ConstBuf = 0;
444
445         dbg_Init(NewBuf);
446
447         return NewBuf;
448 }
449
450 /** 
451  * @ingroup StrBuf_DeConstructors
452  * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
453  * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
454  * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
455  * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe 
456  * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
457  * @returns the new stringbuffer
458  */
459 void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
460 {
461         StrBuf *NewBuf;
462         
463         if (CreateRelpaceMe == NULL)
464                 return;
465
466         if (NoMe != NULL)
467         {
468                 if (*CreateRelpaceMe != NULL)
469                         StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
470                 else 
471                         *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
472                 return;
473         }
474
475         if (CopyFlushMe == NULL)
476         {
477                 if (*CreateRelpaceMe != NULL)
478                         FlushStrBuf(*CreateRelpaceMe);
479                 else 
480                         *CreateRelpaceMe = NewStrBuf();
481                 return;
482         }
483
484         /* 
485          * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
486          * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
487          * be a big IO-Buffer...
488          */
489         if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
490         {
491                 if (*CreateRelpaceMe == NULL)
492                 {
493                         *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
494                         dbg_Init(NewBuf);
495                 }
496                 else 
497                 {
498                         NewBuf = *CreateRelpaceMe;
499                         FlushStrBuf(NewBuf);
500                 }
501                 StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
502         }
503         else
504         {
505                 if (*CreateRelpaceMe == NULL)
506                 {
507                         *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
508                         dbg_Init(NewBuf);
509                 }
510                 else 
511                         NewBuf = *CreateRelpaceMe;
512                 iSwapBuffers (NewBuf, CopyFlushMe);
513         }
514         if (!KeepOriginal)
515                 FlushStrBuf(CopyFlushMe);
516         return;
517 }
518
519 /**
520  * @ingroup StrBuf_DeConstructors
521  * @brief create a new Buffer using an existing c-string
522  * this function should also be used if you want to pre-suggest
523  * the buffer size to allocate in conjunction with ptr == NULL
524  * @param ptr the c-string to copy; may be NULL to create a blank instance
525  * @param nChars How many chars should we copy; -1 if we should measure the length ourselves
526  * @returns the new stringbuffer
527  */
528 StrBuf* NewStrBufPlain(const char* ptr, int nChars)
529 {
530         StrBuf *NewBuf;
531         size_t Siz = BaseStrBufSize;
532         size_t CopySize;
533
534         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
535         if (NewBuf == NULL)
536                 return NULL;
537
538         if (nChars < 0)
539                 CopySize = strlen((ptr != NULL)?ptr:"");
540         else
541                 CopySize = nChars;
542
543         while ((Siz <= CopySize) && (Siz != 0))
544                 Siz *= 2;
545
546         if (Siz == 0)
547         {
548                 free(NewBuf);
549                 return NULL;
550         }
551
552         NewBuf->buf = (char*) malloc(Siz);
553         if (NewBuf->buf == NULL)
554         {
555                 free(NewBuf);
556                 return NULL;
557         }
558         NewBuf->BufSize = Siz;
559         if (ptr != NULL) {
560                 memcpy(NewBuf->buf, ptr, CopySize);
561                 NewBuf->buf[CopySize] = '\0';
562                 NewBuf->BufUsed = CopySize;
563         }
564         else {
565                 NewBuf->buf[0] = '\0';
566                 NewBuf->BufUsed = 0;
567         }
568         NewBuf->ConstBuf = 0;
569
570         dbg_Init(NewBuf);
571
572         return NewBuf;
573 }
574
575 /**
576  * @ingroup StrBuf_DeConstructors
577  * @brief Set an existing buffer from a c-string
578  * @param Buf buffer to load
579  * @param ptr c-string to put into 
580  * @param nChars set to -1 if we should work 0-terminated
581  * @returns the new length of the string
582  */
583 int StrBufPlain(StrBuf *Buf, const char* ptr, int nChars)
584 {
585         size_t Siz;
586         size_t CopySize;
587
588         if (Buf == NULL)
589                 return -1;
590         if (ptr == NULL) {
591                 FlushStrBuf(Buf);
592                 return -1;
593         }
594
595         Siz = Buf->BufSize;
596
597         if (nChars < 0)
598                 CopySize = strlen(ptr);
599         else
600                 CopySize = nChars;
601
602         while ((Siz <= CopySize) && (Siz != 0))
603                 Siz *= 2;
604
605         if (Siz == 0) {
606                 FlushStrBuf(Buf);
607                 return -1;
608         }
609
610         if (Siz != Buf->BufSize)
611                 IncreaseBuf(Buf, 0, Siz);
612         memcpy(Buf->buf, ptr, CopySize);
613         Buf->buf[CopySize] = '\0';
614         Buf->BufUsed = CopySize;
615         Buf->ConstBuf = 0;
616         return CopySize;
617 }
618
619
620 /**
621  * @ingroup StrBuf_DeConstructors
622  * @brief use strbuf as wrapper for a string constant for easy handling
623  * @param StringConstant a string to wrap
624  * @param SizeOfStrConstant should be sizeof(StringConstant)-1
625  */
626 StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
627 {
628         StrBuf *NewBuf;
629
630         NewBuf = (StrBuf*) malloc(sizeof(StrBuf));
631         if (NewBuf == NULL)
632                 return NULL;
633         NewBuf->buf = (char*) StringConstant;
634         NewBuf->BufSize = SizeOfStrConstant;
635         NewBuf->BufUsed = SizeOfStrConstant;
636         NewBuf->ConstBuf = 1;
637
638         dbg_Init(NewBuf);
639
640         return NewBuf;
641 }
642
643
644 /**
645  * @ingroup StrBuf_DeConstructors
646  * @brief flush the content of a Buf; keep its struct
647  * @param buf Buffer to flush
648  */
649 int FlushStrBuf(StrBuf *buf)
650 {
651         if ((buf == NULL) || (buf->buf == NULL))
652                 return -1;
653         if (buf->ConstBuf)
654                 return -1;       
655         buf->buf[0] ='\0';
656         buf->BufUsed = 0;
657         return 0;
658 }
659
660 /**
661  * @ingroup StrBuf_DeConstructors
662  * @brief wipe the content of a Buf thoroughly (overwrite it -> expensive); keep its struct
663  * @param buf Buffer to wipe
664  */
665 int FLUSHStrBuf(StrBuf *buf)
666 {
667         if (buf == NULL)
668                 return -1;
669         if (buf->ConstBuf)
670                 return -1;
671         if (buf->BufUsed > 0) {
672                 memset(buf->buf, 0, buf->BufUsed);
673                 buf->BufUsed = 0;
674         }
675         return 0;
676 }
677
678 #ifdef SIZE_DEBUG
679 int hFreeDbglog = -1;
680 #endif
681 /**
682  * @ingroup StrBuf_DeConstructors
683  * @brief Release a Buffer
684  * Its a double pointer, so it can NULL your pointer
685  * so fancy SIG11 appear instead of random results
686  * @param FreeMe Pointer Pointer to the buffer to free
687  */
688 void FreeStrBuf (StrBuf **FreeMe)
689 {
690         if (*FreeMe == NULL)
691                 return;
692
693         dbg_FreeStrBuf(FreeMe, 'F');
694
695         if (!(*FreeMe)->ConstBuf) 
696                 free((*FreeMe)->buf);
697         free(*FreeMe);
698         *FreeMe = NULL;
699 }
700
701 /**
702  * @ingroup StrBuf_DeConstructors
703  * @brief flatten a Buffer to the Char * we return 
704  * Its a double pointer, so it can NULL your pointer
705  * so fancy SIG11 appear instead of random results
706  * The Callee then owns the buffer and is responsible for freeing it.
707  * @param SmashMe Pointer Pointer to the buffer to release Buf from and free
708  * @returns the pointer of the buffer; Callee owns the memory thereafter.
709  */
710 char *SmashStrBuf (StrBuf **SmashMe)
711 {
712         char *Ret;
713
714         if ((SmashMe == NULL) || (*SmashMe == NULL))
715                 return NULL;
716         
717         dbg_FreeStrBuf(SmashMe, 'S');
718
719         Ret = (*SmashMe)->buf;
720         free(*SmashMe);
721         *SmashMe = NULL;
722         return Ret;
723 }
724
725 /**
726  * @ingroup StrBuf_DeConstructors
727  * @brief Release the buffer
728  * If you want put your StrBuf into a Hash, use this as Destructor.
729  * @param VFreeMe untyped pointer to a StrBuf. be shure to do the right thing [TM]
730  */
731 void HFreeStrBuf (void *VFreeMe)
732 {
733         StrBuf *FreeMe = (StrBuf*)VFreeMe;
734         if (FreeMe == NULL)
735                 return;
736
737         dbg_FreeStrBuf(SmashMe, 'H');
738
739         if (!FreeMe->ConstBuf) 
740                 free(FreeMe->buf);
741         free(FreeMe);
742 }
743
744
745 /*******************************************************************************
746  *                      Simple string transformations                          *
747  *******************************************************************************/
748
749 /**
750  * @ingroup StrBuf
751  * @brief Wrapper around atol
752  */
753 long StrTol(const StrBuf *Buf)
754 {
755         if (Buf == NULL)
756                 return 0;
757         if(Buf->BufUsed > 0)
758                 return atol(Buf->buf);
759         else
760                 return 0;
761 }
762
763 /**
764  * @ingroup StrBuf
765  * @brief Wrapper around atoi
766  */
767 int StrToi(const StrBuf *Buf)
768 {
769         if (Buf == NULL)
770                 return 0;
771         if (Buf->BufUsed > 0)
772                 return atoi(Buf->buf);
773         else
774                 return 0;
775 }
776
777 /**
778  * @ingroup StrBuf
779  * @brief Checks to see if the string is a pure number 
780  * @param Buf The buffer to inspect
781  * @returns 1 if its a pure number, 0, if not.
782  */
783 int StrBufIsNumber(const StrBuf *Buf) {
784         char * pEnd;
785         if ((Buf == NULL) || (Buf->BufUsed == 0)) {
786                 return 0;
787         }
788         strtoll(Buf->buf, &pEnd, 10);
789         if (pEnd == Buf->buf)
790                 return 0;
791         if ((pEnd != NULL) && (pEnd == Buf->buf + Buf->BufUsed))
792                 return 1;
793         if (Buf->buf == pEnd)
794                 return 0;
795         return 0;
796
797
798 /**
799  * @ingroup StrBuf_Filler
800  * @brief modifies a Single char of the Buf
801  * You can point to it via char* or a zero-based integer
802  * @param Buf The buffer to manipulate
803  * @param ptr char* to zero; use NULL if unused
804  * @param nThChar zero based pointer into the string; use -1 if unused
805  * @param PeekValue The Character to place into the position
806  */
807 long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
808 {
809         if (Buf == NULL)
810                 return -1;
811         if (ptr != NULL)
812                 nThChar = ptr - Buf->buf;
813         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
814                 return -1;
815         Buf->buf[nThChar] = PeekValue;
816         return nThChar;
817 }
818
819 /**
820  * @ingroup StrBuf_Filler
821  * @brief modifies a range of chars of the Buf
822  * You can point to it via char* or a zero-based integer
823  * @param Buf The buffer to manipulate
824  * @param ptr char* to zero; use NULL if unused
825  * @param nThChar zero based pointer into the string; use -1 if unused
826  * @param nChars how many chars are to be flushed?
827  * @param PookValue The Character to place into that area
828  */
829 long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char PookValue)
830 {
831         if (Buf == NULL)
832                 return -1;
833         if (ptr != NULL)
834                 nThChar = ptr - Buf->buf;
835         if ((nThChar < 0) || (nThChar > Buf->BufUsed))
836                 return -1;
837         if (nThChar + nChars > Buf->BufUsed)
838                 nChars =  Buf->BufUsed - nThChar;
839
840         memset(Buf->buf + nThChar, PookValue, nChars);
841         /* just to be shure... */
842         Buf->buf[Buf->BufUsed] = 0;
843         return nChars;
844 }
845
846 /**
847  * @ingroup StrBuf_Filler
848  * @brief Append a StringBuffer to the buffer
849  * @param Buf Buffer to modify
850  * @param AppendBuf Buffer to copy at the end of our buffer
851  * @param Offset Should we start copying from an offset?
852  */
853 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
854 {
855         if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
856             (Buf == NULL) || (Buf->buf == NULL))
857                 return;
858
859         if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
860                 IncreaseBuf(Buf, (Buf->BufUsed > 0), AppendBuf->BufUsed + Buf->BufUsed);
861
862         memcpy(Buf->buf + Buf->BufUsed, AppendBuf->buf + Offset, AppendBuf->BufUsed - Offset);
863         Buf->BufUsed += AppendBuf->BufUsed - Offset;
864         Buf->buf[Buf->BufUsed] = '\0';
865 }
866
867
868 // Append a C-String to the buffer
869 // Buf          Buffer to modify
870 // AppendBuf    Buffer to copy at the end of our buffer
871 // AppendSize   number of bytes to copy; set to -1 if we should count it in advance
872 // Offset       Should we start copying from an offset?
873 void StrBufAppendBufPlain(StrBuf *Buf, const char *AppendBuf, long AppendSize, unsigned long Offset) {
874         long aps;
875         long BufSizeRequired;
876
877         if ((AppendBuf == NULL) || (Buf == NULL))
878                 return;
879
880         if (AppendSize < 0) {
881                 aps = strlen(AppendBuf + Offset);
882         }
883         else {
884                 aps = AppendSize - Offset;
885         }
886
887         BufSizeRequired = Buf->BufUsed + aps + 1;
888         if (Buf->BufSize <= BufSizeRequired) {
889                 IncreaseBuf(Buf, (Buf->BufUsed > 0), BufSizeRequired);
890         }
891
892         memcpy(Buf->buf + Buf->BufUsed, 
893                AppendBuf + Offset, 
894                aps);
895         Buf->BufUsed += aps;
896         Buf->buf[Buf->BufUsed] = '\0';
897 }
898
899 /*
900  * @ingroup StrBuf_Filler
901  * @brief sprintf like function appending the formated string to the buffer
902  * vsnprintf version to wrap into own calls
903  * @param Buf Buffer to extend by format and Params
904  * @param format printf alike format to add
905  * @param ap va_list containing the items for format
906  */
907 void StrBufVAppendPrintf(StrBuf *Buf, const char *format, va_list ap)
908 {
909         va_list apl;
910         size_t BufSize;
911         size_t nWritten;
912         size_t Offset;
913         size_t newused;
914
915         if ((Buf == NULL)  || (format == NULL))
916                 return;
917
918         BufSize = Buf->BufSize;
919         nWritten = Buf->BufSize + 1;
920         Offset = Buf->BufUsed;
921         newused = Offset + nWritten;
922         
923         while (newused >= BufSize) {
924                 va_copy(apl, ap);
925                 nWritten = vsnprintf(Buf->buf + Offset, 
926                                      Buf->BufSize - Offset, 
927                                      format, apl);
928                 va_end(apl);
929                 newused = Offset + nWritten;
930                 if (newused >= Buf->BufSize) {
931                         if (IncreaseBuf(Buf, 1, newused) == -1)
932                                 return; /* TODO: error handling? */
933                         newused = Buf->BufSize + 1;
934                 }
935                 else {
936                         Buf->BufUsed = Offset + nWritten;
937                         BufSize = Buf->BufSize;
938                 }
939
940         }
941 }
942
943 /**
944  * @ingroup StrBuf_Filler
945  * @brief sprintf like function appending the formated string to the buffer
946  * @param Buf Buffer to extend by format and Params
947  * @param format printf alike format to add
948  */
949 void StrBufAppendPrintf(StrBuf *Buf, const char *format, ...)
950 {
951         size_t BufSize;
952         size_t nWritten;
953         size_t Offset;
954         size_t newused;
955         va_list arg_ptr;
956         
957         if ((Buf == NULL)  || (format == NULL))
958                 return;
959
960         BufSize = Buf->BufSize;
961         nWritten = Buf->BufSize + 1;
962         Offset = Buf->BufUsed;
963         newused = Offset + nWritten;
964
965         while (newused >= BufSize) {
966                 va_start(arg_ptr, format);
967                 nWritten = vsnprintf(Buf->buf + Buf->BufUsed, 
968                                      Buf->BufSize - Buf->BufUsed, 
969                                      format, arg_ptr);
970                 va_end(arg_ptr);
971                 newused = Buf->BufUsed + nWritten;
972                 if (newused >= Buf->BufSize) {
973                         if (IncreaseBuf(Buf, 1, newused) == -1)
974                                 return; /* TODO: error handling? */
975                         newused = Buf->BufSize + 1;
976                 }
977                 else {
978                         Buf->BufUsed += nWritten;
979                         BufSize = Buf->BufSize;
980                 }
981
982         }
983 }
984
985 /**
986  * @ingroup StrBuf_Filler
987  * @brief sprintf like function putting the formated string into the buffer
988  * @param Buf Buffer to extend by format and Parameters
989  * @param format printf alike format to add
990  */
991 void StrBufPrintf(StrBuf *Buf, const char *format, ...)
992 {
993         size_t nWritten;
994         va_list arg_ptr;
995         
996         if ((Buf == NULL)  || (format == NULL))
997                 return;
998
999         nWritten = Buf->BufSize + 1;
1000         while (nWritten >= Buf->BufSize) {
1001                 va_start(arg_ptr, format);
1002                 nWritten = vsnprintf(Buf->buf, Buf->BufSize, format, arg_ptr);
1003                 va_end(arg_ptr);
1004                 if (nWritten >= Buf->BufSize) {
1005                         if (IncreaseBuf(Buf, 0, 0) == -1)
1006                                 return; /* TODO: error handling? */
1007                         nWritten = Buf->BufSize + 1;
1008                         continue;
1009                 }
1010                 Buf->BufUsed = nWritten ;
1011         }
1012 }
1013
1014 /**
1015  * @ingroup StrBuf_Filler
1016  * @brief Callback for cURL to append the webserver reply to a buffer
1017  * @param ptr pre-defined by the cURL API; see man 3 curl for mre info
1018  * @param size pre-defined by the cURL API; see man 3 curl for mre info
1019  * @param nmemb pre-defined by the cURL API; see man 3 curl for mre info
1020  * @param stream pre-defined by the cURL API; see man 3 curl for mre info
1021  */
1022 size_t CurlFillStrBuf_callback(void *ptr, size_t size, size_t nmemb, void *stream)
1023 {
1024
1025         StrBuf *Target;
1026
1027         Target = stream;
1028         if (ptr == NULL)
1029                 return 0;
1030
1031         StrBufAppendBufPlain(Target, ptr, size * nmemb, 0);
1032         return size * nmemb;
1033 }
1034
1035
1036 /**
1037  * @ingroup StrBuf
1038  * @brief extracts a substring from Source into dest
1039  * @param dest buffer to place substring into
1040  * @param Source string to copy substring from
1041  * @param Offset chars to skip from start
1042  * @param nChars number of chars to copy
1043  * @returns the number of chars copied; may be different from nChars due to the size of Source
1044  */
1045 int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t nChars)
1046 {
1047         size_t NCharsRemain;
1048         if (Offset > Source->BufUsed)
1049         {
1050                 if (dest != NULL)
1051                         FlushStrBuf(dest);
1052                 return 0;
1053         }
1054         if (Offset + nChars < Source->BufUsed)
1055         {
1056                 if ((nChars >= dest->BufSize) && 
1057                     (IncreaseBuf(dest, 0, nChars + 1) == -1))
1058                         return 0;
1059                 memcpy(dest->buf, Source->buf + Offset, nChars);
1060                 dest->BufUsed = nChars;
1061                 dest->buf[dest->BufUsed] = '\0';
1062                 return nChars;
1063         }
1064         NCharsRemain = Source->BufUsed - Offset;
1065         if ((NCharsRemain  >= dest->BufSize) && 
1066             (IncreaseBuf(dest, 0, NCharsRemain + 1) == -1))
1067                 return 0;
1068         memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
1069         dest->BufUsed = NCharsRemain;
1070         dest->buf[dest->BufUsed] = '\0';
1071         return NCharsRemain;
1072 }
1073
1074 /**
1075  * @ingroup StrBuf
1076  * @brief Cut nChars from the start of the string
1077  * @param Buf Buffer to modify
1078  * @param nChars how many chars should be skipped?
1079  */
1080 void StrBufCutLeft(StrBuf *Buf, int nChars)
1081 {
1082         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1083         if (nChars >= Buf->BufUsed) {
1084                 FlushStrBuf(Buf);
1085                 return;
1086         }
1087         memmove(Buf->buf, Buf->buf + nChars, Buf->BufUsed - nChars);
1088         Buf->BufUsed -= nChars;
1089         Buf->buf[Buf->BufUsed] = '\0';
1090 }
1091
1092 /**
1093  * @ingroup StrBuf
1094  * @brief Cut the trailing n Chars from the string
1095  * @param Buf Buffer to modify
1096  * @param nChars how many chars should be trunkated?
1097  */
1098 void StrBufCutRight(StrBuf *Buf, int nChars)
1099 {
1100         if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
1101                 return;
1102
1103         if (nChars >= Buf->BufUsed) {
1104                 FlushStrBuf(Buf);
1105                 return;
1106         }
1107         Buf->BufUsed -= nChars;
1108         Buf->buf[Buf->BufUsed] = '\0';
1109 }
1110
1111 /**
1112  * @ingroup StrBuf
1113  * @brief Cut the string after n Chars
1114  * @param Buf Buffer to modify
1115  * @param AfternChars after how many chars should we trunkate the string?
1116  * @param At if non-null and points inside of our string, cut it there.
1117  */
1118 void StrBufCutAt(StrBuf *Buf, int AfternChars, const char *At)
1119 {
1120         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1121         if (At != NULL){
1122                 AfternChars = At - Buf->buf;
1123         }
1124
1125         if ((AfternChars < 0) || (AfternChars >= Buf->BufUsed))
1126                 return;
1127         Buf->BufUsed = AfternChars;
1128         Buf->buf[Buf->BufUsed] = '\0';
1129 }
1130
1131
1132 /**
1133  * @ingroup StrBuf
1134  * @brief Strip leading and trailing spaces from a string; with premeasured and adjusted length.
1135  * @param Buf the string to modify
1136  */
1137 void StrBufTrim(StrBuf *Buf)
1138 {
1139         int delta = 0;
1140         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1141
1142         while ((Buf->BufUsed > 0) &&
1143                isspace(Buf->buf[Buf->BufUsed - 1]))
1144         {
1145                 Buf->BufUsed --;
1146         }
1147         Buf->buf[Buf->BufUsed] = '\0';
1148
1149         if (Buf->BufUsed == 0) return;
1150
1151         while ((Buf->BufUsed > delta) && (isspace(Buf->buf[delta]))){
1152                 delta ++;
1153         }
1154         if (delta > 0) StrBufCutLeft(Buf, delta);
1155 }
1156 /**
1157  * @ingroup StrBuf
1158  * @brief changes all spaces in the string  (tab, linefeed...) to Blank (0x20)
1159  * @param Buf the string to modify
1160  */
1161 void StrBufSpaceToBlank(StrBuf *Buf)
1162 {
1163         char *pche, *pch;
1164
1165         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1166
1167         pch = Buf->buf;
1168         pche = pch + Buf->BufUsed;
1169         while (pch < pche) 
1170         {
1171                 if (isspace(*pch))
1172                         *pch = ' ';
1173                 pch ++;
1174         }
1175 }
1176
1177 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
1178 {
1179         const char *pLeft;
1180         const char *pRight;
1181
1182         if ((Buf == NULL) || (Buf->buf == NULL)) {
1183                 return;
1184         }
1185
1186         pRight = strchr(Buf->buf, rightboundary);
1187         if (pRight != NULL) {
1188                 StrBufCutAt(Buf, 0, pRight);
1189         }
1190
1191         pLeft = strrchr(ChrPtr(Buf), leftboundary);
1192         if (pLeft != NULL) {
1193                 StrBufCutLeft(Buf, pLeft - Buf->buf + 1);
1194         }
1195 }
1196
1197
1198 /**
1199  * @ingroup StrBuf_Filler
1200  * @brief uppercase the contents of a buffer
1201  * @param Buf the buffer to translate
1202  */
1203 void StrBufUpCase(StrBuf *Buf) 
1204 {
1205         char *pch, *pche;
1206
1207         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1208
1209         pch = Buf->buf;
1210         pche = pch + Buf->BufUsed;
1211         while (pch < pche) {
1212                 *pch = toupper(*pch);
1213                 pch ++;
1214         }
1215 }
1216
1217
1218 /**
1219  * @ingroup StrBuf_Filler
1220  * @brief lowercase the contents of a buffer
1221  * @param Buf the buffer to translate
1222  */
1223 void StrBufLowerCase(StrBuf *Buf) 
1224 {
1225         char *pch, *pche;
1226
1227         if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
1228
1229         pch = Buf->buf;
1230         pche = pch + Buf->BufUsed;
1231         while (pch < pche) {
1232                 *pch = tolower(*pch);
1233                 pch ++;
1234         }
1235 }
1236
1237
1238 /*******************************************************************************
1239  *           a tokenizer that kills, maims, and destroys                       *
1240  *******************************************************************************/
1241
1242 /**
1243  * @ingroup StrBuf_Tokenizer
1244  * @brief Replace a token at a given place with a given length by another token with given length
1245  * @param Buf String where to work on
1246  * @param where where inside of the Buf is the search-token
1247  * @param HowLong How long is the token to be replaced
1248  * @param Repl Token to insert at 'where'
1249  * @param ReplLen Length of repl
1250  * @returns -1 if fail else length of resulting Buf
1251  */
1252 int StrBufReplaceToken(StrBuf *Buf, long where, long HowLong, 
1253                        const char *Repl, long ReplLen)
1254 {
1255
1256         if ((Buf == NULL) || 
1257             (where > Buf->BufUsed) ||
1258             (where + HowLong > Buf->BufUsed))
1259                 return -1;
1260
1261         if (where + ReplLen - HowLong > Buf->BufSize)
1262                 if (IncreaseBuf(Buf, 1, Buf->BufUsed + ReplLen) < 0)
1263                         return -1;
1264
1265         memmove(Buf->buf + where + ReplLen, 
1266                 Buf->buf + where + HowLong,
1267                 Buf->BufUsed - where - HowLong);
1268                                                 
1269         memcpy(Buf->buf + where, 
1270                Repl, ReplLen);
1271
1272         Buf->BufUsed += ReplLen - HowLong;
1273
1274         return Buf->BufUsed;
1275 }
1276
1277 /**
1278  * @ingroup StrBuf_Tokenizer
1279  * @brief Counts the numbmer of tokens in a buffer
1280  * @param source String to count tokens in
1281  * @param tok    Tokenizer char to count
1282  * @returns numbers of tokenizer chars found
1283  */
1284 int StrBufNum_tokens(const StrBuf *source, char tok)
1285 {
1286         char *pch, *pche;
1287         long NTokens;
1288         if ((source == NULL) || (source->BufUsed == 0))
1289                 return 0;
1290         if ((source->BufUsed == 1) && (*source->buf == tok))
1291                 return 2;
1292         NTokens = 1;
1293         pch = source->buf;
1294         pche = pch + source->BufUsed;
1295         while (pch < pche)
1296         {
1297                 if (*pch == tok)
1298                         NTokens ++;
1299                 pch ++;
1300         }
1301         return NTokens;
1302 }
1303
1304 /**
1305  * @ingroup StrBuf_Tokenizer
1306  * @brief a string tokenizer
1307  * @param Source StringBuffer to read into
1308  * @param parmnum n'th Parameter to remove
1309  * @param separator tokenizer character
1310  * @returns -1 if not found, else length of token.
1311  */
1312 int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
1313 {
1314         int ReducedBy;
1315         char *d, *s, *end;              /* dest, source */
1316         int count = 0;
1317
1318         /* Find desired @parameter */
1319         end = Source->buf + Source->BufUsed;
1320         d = Source->buf;
1321         while ((d <= end) && 
1322                (count < parmnum))
1323         {
1324                 /* End of string, bail! */
1325                 if (!*d) {
1326                         d = NULL;
1327                         break;
1328                 }
1329                 if (*d == separator) {
1330                         count++;
1331                 }
1332                 d++;
1333         }
1334         if ((d == NULL) || (d >= end))
1335                 return 0;               /* @Parameter not found */
1336
1337         /* Find next @parameter */
1338         s = d;
1339         while ((s <= end) && 
1340                (*s && *s != separator))
1341         {
1342                 s++;
1343         }
1344         if (*s == separator)
1345                 s++;
1346         ReducedBy = d - s;
1347
1348         /* Hack and slash */
1349         if (s >= end) {
1350                 return 0;
1351         }
1352         else if (*s) {
1353                 memmove(d, s, Source->BufUsed - (s - Source->buf));
1354                 Source->BufUsed += ReducedBy;
1355                 Source->buf[Source->BufUsed] = '\0';
1356         }
1357         else if (d == Source->buf) {
1358                 *d = 0;
1359                 Source->BufUsed = 0;
1360         }
1361         else {
1362                 *--d = '\0';
1363                 Source->BufUsed += ReducedBy;
1364         }
1365         /*
1366         while (*s) {
1367                 *d++ = *s++;
1368         }
1369         *d = 0;
1370         */
1371         return ReducedBy;
1372 }
1373
1374 int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
1375 {
1376         const StrBuf Temp = {
1377                 (char*)Source,
1378                 SourceLen,
1379                 SourceLen,
1380                 1
1381 #ifdef SIZE_DEBUG
1382                 ,
1383                 0,
1384                 "",
1385                 ""
1386 #endif
1387         };
1388
1389         return StrBufExtract_token(dest, &Temp, parmnum, separator);
1390 }
1391
1392 /**
1393  * @ingroup StrBuf_Tokenizer
1394  * @brief a string tokenizer
1395  * @param dest Destination StringBuffer
1396  * @param Source StringBuffer to read into
1397  * @param parmnum n'th Parameter to extract
1398  * @param separator tokenizer character
1399  * @returns -1 if not found, else length of token.
1400  */
1401 int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char separator)
1402 {
1403         const char *s, *e;              //* source * /
1404         int len = 0;                    //* running total length of extracted string * /
1405         int current_token = 0;          //* token currently being processed * /
1406          
1407         if (dest != NULL) {
1408                 dest->buf[0] = '\0';
1409                 dest->BufUsed = 0;
1410         }
1411         else
1412                 return(-1);
1413
1414         if ((Source == NULL) || (Source->BufUsed ==0)) {
1415                 return(-1);
1416         }
1417         s = Source->buf;
1418         e = s + Source->BufUsed;
1419
1420         //cit_backtrace();
1421         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1422
1423         while ((s < e) && !IsEmptyStr(s)) {
1424                 if (*s == separator) {
1425                         ++current_token;
1426                 }
1427                 if (len >= dest->BufSize) {
1428                         dest->BufUsed = len;
1429                         if (IncreaseBuf(dest, 1, -1) < 0) {
1430                                 dest->BufUsed --;
1431                                 break;
1432                         }
1433                 }
1434                 if ( (current_token == parmnum) && 
1435                      (*s != separator)) {
1436                         dest->buf[len] = *s;
1437                         ++len;
1438                 }
1439                 else if (current_token > parmnum) {
1440                         break;
1441                 }
1442                 ++s;
1443         }
1444         
1445         dest->buf[len] = '\0';
1446         dest->BufUsed = len;
1447                 
1448         if (current_token < parmnum) {
1449                 //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
1450                 return(-1);
1451         }
1452         //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
1453         return(len);
1454 }
1455
1456
1457
1458
1459
1460 /**
1461  * @ingroup StrBuf_Tokenizer
1462  * @brief a string tokenizer to fetch an integer
1463  * @param Source String containing tokens
1464  * @param parmnum n'th Parameter to extract
1465  * @param separator tokenizer character
1466  * @returns 0 if not found, else integer representation of the token
1467  */
1468 int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
1469 {
1470         StrBuf tmp;
1471         char buf[64];
1472         
1473         tmp.buf = buf;
1474         buf[0] = '\0';
1475         tmp.BufSize = 64;
1476         tmp.BufUsed = 0;
1477         tmp.ConstBuf = 1;
1478         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1479                 return(atoi(buf));
1480         else
1481                 return 0;
1482 }
1483
1484 /**
1485  * @ingroup StrBuf_Tokenizer
1486  * @brief a string tokenizer to fetch a long integer
1487  * @param Source String containing tokens
1488  * @param parmnum n'th Parameter to extract
1489  * @param separator tokenizer character
1490  * @returns 0 if not found, else long integer representation of the token
1491  */
1492 long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
1493 {
1494         StrBuf tmp;
1495         char buf[64];
1496         
1497         tmp.buf = buf;
1498         buf[0] = '\0';
1499         tmp.BufSize = 64;
1500         tmp.BufUsed = 0;
1501         tmp.ConstBuf = 1;
1502         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
1503                 return(atoi(buf));
1504         else
1505                 return 0;
1506 }
1507
1508
1509 /**
1510  * @ingroup StrBuf_Tokenizer
1511  * @brief a string tokenizer to fetch an unsigned long
1512  * @param Source String containing tokens
1513  * @param parmnum n'th Parameter to extract
1514  * @param separator tokenizer character
1515  * @returns 0 if not found, else unsigned long representation of the token
1516  */
1517 unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, char separator)
1518 {
1519         StrBuf tmp;
1520         char buf[64];
1521         char *pnum;
1522         
1523         tmp.buf = buf;
1524         buf[0] = '\0';
1525         tmp.BufSize = 64;
1526         tmp.BufUsed = 0;
1527         tmp.ConstBuf = 1;
1528         if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
1529                 pnum = &buf[0];
1530                 if (*pnum == '-')
1531                         pnum ++;
1532                 return (unsigned long) atol(pnum);
1533         }
1534         else 
1535                 return 0;
1536 }
1537
1538
1539
1540 /**
1541  * @ingroup StrBuf_NextTokenizer
1542  * @brief a string tokenizer; Bounds checker
1543  *  function to make shure whether StrBufExtract_NextToken and friends have reached the end of the string.
1544  * @param Source our tokenbuffer
1545  * @param pStart the token iterator pointer to inspect
1546  * @returns whether the revolving pointer is inside of the search range
1547  */
1548 int StrBufHaveNextToken(const StrBuf *Source, const char **pStart)
1549 {
1550         if ((Source == NULL) || 
1551             (*pStart == StrBufNOTNULL) ||
1552             (Source->BufUsed == 0))
1553         {
1554                 return 0;
1555         }
1556         if (*pStart == NULL)
1557         {
1558                 return 1;
1559         }
1560         else if (*pStart > Source->buf + Source->BufUsed)
1561         {
1562                 return 0;
1563         }
1564         else if (*pStart <= Source->buf)
1565         {
1566                 return 0;
1567         }
1568
1569         return 1;
1570 }
1571
1572 /**
1573  * @ingroup StrBuf_NextTokenizer
1574  * @brief a string tokenizer
1575  * @param dest Destination StringBuffer
1576  * @param Source StringBuffer to read into
1577  * @param pStart pointer to the end of the last token. Feed with NULL on start.
1578  * @param separator tokenizer 
1579  * @returns -1 if not found, else length of token.
1580  */
1581 int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
1582 {
1583         const char *s;          /* source */
1584         const char *EndBuffer;  /* end stop of source buffer */
1585         int current_token = 0;  /* token currently being processed */
1586         int len = 0;            /* running total length of extracted string */
1587
1588         if ((Source          == NULL) || 
1589             (Source->BufUsed == 0)      ) 
1590         {
1591                 *pStart = StrBufNOTNULL;
1592                 if (dest != NULL)
1593                         FlushStrBuf(dest);
1594                 return -1;
1595         }
1596          
1597         EndBuffer = Source->buf + Source->BufUsed;
1598
1599         if (dest != NULL) 
1600         {
1601                 dest->buf[0] = '\0';
1602                 dest->BufUsed = 0;
1603         }
1604         else
1605         {
1606                 *pStart = EndBuffer + 1;
1607                 return -1;
1608         }
1609
1610         if (*pStart == NULL)
1611         {
1612                 *pStart = Source->buf; /* we're starting to examine this buffer. */
1613         }
1614         else if ((*pStart < Source->buf) || 
1615                  (*pStart > EndBuffer  )   ) 
1616         {
1617                 return -1; /* no more tokens to find. */
1618         }
1619
1620         s = *pStart;
1621         /* start to find the next token */
1622         while ((s <= EndBuffer)      && 
1623                (current_token == 0) ) 
1624         {
1625                 if (*s == separator) 
1626                 {
1627                         /* we found the next token */
1628                         ++current_token;
1629                 }
1630
1631                 if (len >= dest->BufSize) 
1632                 {
1633                         /* our Dest-buffer isn't big enough, increase it. */
1634                         dest->BufUsed = len;
1635
1636                         if (IncreaseBuf(dest, 1, -1) < 0) {
1637                                 /* WHUT? no more mem? bail out. */
1638                                 s = EndBuffer;
1639                                 dest->BufUsed --;
1640                                 break;
1641                         }
1642                 }
1643
1644                 if ( (current_token == 0 ) &&   /* are we in our target token? */
1645                      (!IsEmptyStr(s)     ) &&
1646                      (separator     != *s)    ) /* don't copy the token itself */
1647                 {
1648                         dest->buf[len] = *s;    /* Copy the payload */
1649                         ++len;                  /* remember the bigger size. */
1650                 }
1651
1652                 ++s;
1653         }
1654
1655         /* did we reach the end? */
1656         if ((s > EndBuffer)) {
1657                 EndBuffer = StrBufNOTNULL;
1658                 *pStart = EndBuffer;
1659         }
1660         else {
1661                 *pStart = s;  /* remember the position for the next run */
1662         }
1663
1664         /* sanitize our extracted token */
1665         dest->buf[len] = '\0';
1666         dest->BufUsed  = len;
1667
1668         return (len);
1669 }
1670
1671
1672 /**
1673  * @ingroup StrBuf_NextTokenizer
1674  * @brief a string tokenizer
1675  * @param Source StringBuffer to read from
1676  * @param pStart pointer to the end of the last token. Feed with NULL.
1677  * @param separator tokenizer character
1678  * @param nTokens number of tokens to fastforward over
1679  * @returns -1 if not found, else length of token.
1680  */
1681 int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
1682 {
1683         const char *s, *EndBuffer;      //* source * /
1684         int len = 0;                    //* running total length of extracted string * /
1685         int current_token = 0;          //* token currently being processed * /
1686
1687         if ((Source == NULL) || 
1688             (Source->BufUsed ==0)) {
1689                 return(-1);
1690         }
1691         if (nTokens == 0)
1692                 return Source->BufUsed;
1693
1694         if (*pStart == NULL)
1695                 *pStart = Source->buf;
1696
1697         EndBuffer = Source->buf + Source->BufUsed;
1698
1699         if ((*pStart < Source->buf) || 
1700             (*pStart >  EndBuffer)) {
1701                 return (-1);
1702         }
1703
1704
1705         s = *pStart;
1706
1707         //cit_backtrace();
1708         //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
1709
1710         while ((s < EndBuffer) && !IsEmptyStr(s)) {
1711                 if (*s == separator) {
1712                         ++current_token;
1713                 }
1714                 if (current_token >= nTokens) {
1715                         break;
1716                 }
1717                 ++s;
1718         }
1719         *pStart = s;
1720         (*pStart) ++;
1721
1722         return(len);
1723 }
1724
1725 /**
1726  * @ingroup StrBuf_NextTokenizer
1727  * @brief a string tokenizer to fetch an integer
1728  * @param Source StringBuffer to read from
1729  * @param pStart Cursor on the tokenstring
1730  * @param separator tokenizer character
1731  * @returns 0 if not found, else integer representation of the token
1732  */
1733 int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
1734 {
1735         StrBuf tmp;
1736         char buf[64];
1737         
1738         tmp.buf = buf;
1739         buf[0] = '\0';
1740         tmp.BufSize = 64;
1741         tmp.BufUsed = 0;
1742         tmp.ConstBuf = 1;
1743         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1744                 return(atoi(buf));
1745         else
1746                 return 0;
1747 }
1748
1749 /**
1750  * @ingroup StrBuf_NextTokenizer
1751  * @brief a string tokenizer to fetch a long integer
1752  * @param Source StringBuffer to read from
1753  * @param pStart Cursor on the tokenstring
1754  * @param separator tokenizer character
1755  * @returns 0 if not found, else long integer representation of the token
1756  */
1757 long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
1758 {
1759         StrBuf tmp;
1760         char buf[64];
1761         
1762         tmp.buf = buf;
1763         buf[0] = '\0';
1764         tmp.BufSize = 64;
1765         tmp.BufUsed = 0;
1766         tmp.ConstBuf = 1;
1767         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
1768                 return(atoi(buf));
1769         else
1770                 return 0;
1771 }
1772
1773
1774 /**
1775  * @ingroup StrBuf_NextTokenizer
1776  * @brief a string tokenizer to fetch an unsigned long
1777  * @param Source StringBuffer to read from
1778  * @param pStart Cursor on the tokenstring
1779  * @param separator tokenizer character
1780  * @returns 0 if not found, else unsigned long representation of the token
1781  */
1782 unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
1783 {
1784         StrBuf tmp;
1785         char buf[64];
1786         char *pnum;
1787         
1788         tmp.buf = buf;
1789         buf[0] = '\0';
1790         tmp.BufSize = 64;
1791         tmp.BufUsed = 0;
1792         tmp.ConstBuf = 1;
1793         if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
1794                 pnum = &buf[0];
1795                 if (*pnum == '-')
1796                         pnum ++;
1797                 return (unsigned long) atol(pnum);
1798         }
1799         else 
1800                 return 0;
1801 }
1802
1803
1804
1805
1806
1807 /*******************************************************************************
1808  *                             Escape Appending                                *
1809  *******************************************************************************/
1810
1811 /** 
1812  * @ingroup StrBuf_DeEnCoder
1813  * @brief Escape a string for feeding out as a URL while appending it to a Buffer
1814  * @param OutBuf the output buffer
1815  * @param In Buffer to encode
1816  * @param PlainIn way in from plain old c strings
1817  */
1818 void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1819 {
1820         const char *pch, *pche;
1821         char *pt, *pte;
1822         int len;
1823         
1824         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1825                 return;
1826         if (PlainIn != NULL) {
1827                 len = strlen(PlainIn);
1828                 pch = PlainIn;
1829                 pche = pch + len;
1830         }
1831         else {
1832                 pch = In->buf;
1833                 pche = pch + In->BufUsed;
1834                 len = In->BufUsed;
1835         }
1836
1837         if (len == 0) 
1838                 return;
1839
1840         pt = OutBuf->buf + OutBuf->BufUsed;
1841         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1842
1843         while (pch < pche) {
1844                 if (pt >= pte) {
1845                         IncreaseBuf(OutBuf, 1, -1);
1846                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1847                         pt = OutBuf->buf + OutBuf->BufUsed;
1848                 }
1849
1850                 if((*pch >= 'a' && *pch <= 'z') ||
1851                    (*pch >= '@' && *pch <= 'Z') || /* @ A-Z */
1852                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1853                    (*pch == '!') || (*pch == '_') || 
1854                    (*pch == ',') || (*pch == '.'))
1855                 {
1856                         *(pt++) = *(pch++);
1857                         OutBuf->BufUsed++;
1858                 }                       
1859                 else {
1860                         *pt = '%';
1861                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1862                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1863                         pt += 3;
1864                         OutBuf->BufUsed += 3;
1865                         pch ++;
1866                 }
1867         }
1868         *pt = '\0';
1869 }
1870
1871 /** 
1872  * @ingroup StrBuf_DeEnCoder
1873  * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
1874  * @param OutBuf the output buffer
1875  * @param In Buffer to encode
1876  * @param PlainIn way in from plain old c strings
1877  */
1878 void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
1879 {
1880         const char *pch, *pche;
1881         char *pt, *pte;
1882         int len;
1883         
1884         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1885                 return;
1886         if (PlainIn != NULL) {
1887                 len = strlen(PlainIn);
1888                 pch = PlainIn;
1889                 pche = pch + len;
1890         }
1891         else {
1892                 pch = In->buf;
1893                 pche = pch + In->BufUsed;
1894                 len = In->BufUsed;
1895         }
1896
1897         if (len == 0) 
1898                 return;
1899
1900         pt = OutBuf->buf + OutBuf->BufUsed;
1901         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1902
1903         while (pch < pche) {
1904                 if (pt >= pte) {
1905                         IncreaseBuf(OutBuf, 1, -1);
1906                         pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
1907                         pt = OutBuf->buf + OutBuf->BufUsed;
1908                 }
1909
1910                 if((*pch >= 'a' && *pch <= 'z') ||
1911                    (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
1912                    (*pch >= '0' && *pch <= ':') || /* 0-9 : */
1913                    (*pch == '!') || (*pch == '_') || 
1914                    (*pch == ',') || (*pch == '.'))
1915                 {
1916                         *(pt++) = *(pch++);
1917                         OutBuf->BufUsed++;
1918                 }                       
1919                 else {
1920                         *pt = '%';
1921                         *(pt + 1) = HexList[(unsigned char)*pch][0];
1922                         *(pt + 2) = HexList[(unsigned char)*pch][1];
1923                         pt += 3;
1924                         OutBuf->BufUsed += 3;
1925                         pch ++;
1926                 }
1927         }
1928         *pt = '\0';
1929 }
1930
1931 /** 
1932  * @ingroup StrBuf_DeEnCoder
1933  * @brief append a string with characters having a special meaning in xml encoded to the buffer
1934  * @param OutBuf the output buffer
1935  * @param In Buffer to encode
1936  * @param PlainIn way in from plain old c strings
1937  * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
1938  * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
1939  */
1940 void StrBufXMLEscAppend(StrBuf *OutBuf,
1941                         const StrBuf *In,
1942                         const char *PlainIn,
1943                         long PlainInLen,
1944                         int OverrideLowChars)
1945 {
1946         const char *pch, *pche;
1947         char *pt, *pte;
1948         int IsUtf8Sequence;
1949         int len;
1950
1951         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
1952                 return;
1953         if (PlainIn != NULL) {
1954                 if (PlainInLen < 0)
1955                         len = strlen((const char*)PlainIn);
1956                 else
1957                         len = PlainInLen;
1958                 pch = PlainIn;
1959                 pche = pch + len;
1960         }
1961         else {
1962                 pch = (const char*)In->buf;
1963                 pche = pch + In->BufUsed;
1964                 len = In->BufUsed;
1965         }
1966
1967         if (len == 0)
1968                 return;
1969
1970         pt = OutBuf->buf + OutBuf->BufUsed;
1971         /**< we max append 6 chars at once plus the \0 */
1972         pte = OutBuf->buf + OutBuf->BufSize - 6;
1973
1974         while (pch < pche) {
1975                 if (pt >= pte) {
1976                         OutBuf->BufUsed = pt - OutBuf->buf;
1977                         IncreaseBuf(OutBuf, 1, -1);
1978                         pte = OutBuf->buf + OutBuf->BufSize - 6;
1979                         /**< we max append 3 chars at once plus the \0 */
1980
1981                         pt = OutBuf->buf + OutBuf->BufUsed;
1982                 }
1983
1984                 if (*pch == '<') {
1985                         memcpy(pt, HKEY("&lt;"));
1986                         pt += 4;
1987                         pch ++;
1988                 }
1989                 else if (*pch == '>') {
1990                         memcpy(pt, HKEY("&gt;"));
1991                         pt += 4;
1992                         pch ++;
1993                 }
1994                 else if (*pch == '&') {
1995                         memcpy(pt, HKEY("&amp;"));
1996                         pt += 5;
1997                         pch++;
1998                 }
1999                 else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
2000                         *pt = *pch;
2001                         pt++; pch++;
2002                 }
2003                 else {
2004                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(pch, pche);
2005                         if (IsUtf8Sequence)
2006                         {
2007                                 while ((IsUtf8Sequence > 0) && 
2008                                        (pch < pche))
2009                                 {
2010                                         *pt = *pch;
2011                                         pt ++;
2012                                         pch ++;
2013                                         --IsUtf8Sequence;
2014                                 }
2015                         }
2016                         else
2017                         {
2018                                 *pt = '&';
2019                                 pt++;
2020                                 *pt = HexList[*(unsigned char*)pch][0];
2021                                 pt ++;
2022                                 *pt = HexList[*(unsigned char*)pch][1];
2023                                 pt ++; pch ++;
2024                                 *pt = '&';
2025                                 pt++;
2026                                 pch ++;
2027                         }
2028                 }
2029         }
2030         *pt = '\0';
2031         OutBuf->BufUsed = pt - OutBuf->buf;
2032 }
2033
2034
2035 /** 
2036  * @ingroup StrBuf_DeEnCoder
2037  * @brief append a string in hex encoding to the buffer
2038  * @param OutBuf the output buffer
2039  * @param In Buffer to encode
2040  * @param PlainIn way in from plain old c strings
2041  * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
2042  */
2043 void StrBufHexEscAppend(StrBuf *OutBuf, const StrBuf *In, const unsigned char *PlainIn, long PlainInLen)
2044 {
2045         const unsigned char *pch, *pche;
2046         char *pt, *pte;
2047         int len;
2048         
2049         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2050                 return;
2051         if (PlainIn != NULL) {
2052                 if (PlainInLen < 0)
2053                         len = strlen((const char*)PlainIn);
2054                 else
2055                         len = PlainInLen;
2056                 pch = PlainIn;
2057                 pche = pch + len;
2058         }
2059         else {
2060                 pch = (const unsigned char*)In->buf;
2061                 pche = pch + In->BufUsed;
2062                 len = In->BufUsed;
2063         }
2064
2065         if (len == 0) 
2066                 return;
2067
2068         pt = OutBuf->buf + OutBuf->BufUsed;
2069         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2070
2071         while (pch < pche) {
2072                 if (pt >= pte) {
2073                         IncreaseBuf(OutBuf, 1, -1);
2074                         pte = OutBuf->buf + OutBuf->BufSize - 3; /**< we max append 3 chars at once plus the \0 */
2075                         pt = OutBuf->buf + OutBuf->BufUsed;
2076                 }
2077
2078                 *pt = HexList[*pch][0];
2079                 pt ++;
2080                 *pt = HexList[*pch][1];
2081                 pt ++; pch ++; OutBuf->BufUsed += 2;
2082         }
2083         *pt = '\0';
2084 }
2085
2086 void StrBufBase64Append(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn, long PlainInLen, int linebreaks)
2087 {
2088         const char *pch;
2089         char *pt;
2090         int len;
2091         long ExpectLen;
2092         
2093         if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
2094                 return;
2095         if (PlainIn != NULL) {
2096                 if (PlainInLen < 0)
2097                         len = strlen(PlainIn);
2098                 else
2099                         len = PlainInLen;
2100                 pch = PlainIn;
2101         }
2102         else {
2103                 pch = In->buf;
2104                 len = In->BufUsed;
2105         }
2106
2107         if (len == 0) 
2108                 return;
2109
2110         ExpectLen = ((len * 134) / 100) + OutBuf->BufUsed;
2111
2112         if (ExpectLen > OutBuf->BufSize)
2113                 if (IncreaseBuf(OutBuf, 1, ExpectLen) < ExpectLen)
2114                         return;
2115
2116         pt = OutBuf->buf + OutBuf->BufUsed;
2117
2118         len = CtdlEncodeBase64(pt, pch, len, linebreaks);
2119
2120         pt += len;
2121         OutBuf->BufUsed += len;
2122         *pt = '\0';
2123 }
2124
2125 /** 
2126  * @ingroup StrBuf_DeEnCoder
2127  * @brief append a string in hex encoding to the buffer
2128  * @param OutBuf the output buffer
2129  * @param In Buffer to encode
2130  * @param PlainIn way in from plain old c strings
2131  */
2132 void StrBufHexescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
2133 {
2134         StrBufHexEscAppend(OutBuf, In, (const unsigned char*) PlainIn, -1);
2135 }
2136
2137 /**
2138  * @ingroup StrBuf_DeEnCoder
2139  * @brief Append a string, escaping characters which have meaning in HTML.  
2140  *
2141  * @param Target        target buffer
2142  * @param Source        source buffer; set to NULL if you just have a C-String
2143  * @param PlainIn       Plain-C string to append; set to NULL if unused
2144  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2145  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2146  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2147  */
2148 long StrEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2149 {
2150         const char *aptr, *eiptr;
2151         char *bptr, *eptr;
2152         long len;
2153
2154         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2155                 return -1;
2156
2157         if (PlainIn != NULL) {
2158                 aptr = PlainIn;
2159                 len = strlen(PlainIn);
2160                 eiptr = aptr + len;
2161         }
2162         else {
2163                 aptr = Source->buf;
2164                 eiptr = aptr + Source->BufUsed;
2165                 len = Source->BufUsed;
2166         }
2167
2168         if (len == 0) 
2169                 return -1;
2170
2171         bptr = Target->buf + Target->BufUsed;
2172         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2173
2174         while (aptr < eiptr){
2175                 if(bptr >= eptr) {
2176                         IncreaseBuf(Target, 1, -1);
2177                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2178                         bptr = Target->buf + Target->BufUsed;
2179                 }
2180                 if (*aptr == '<') {
2181                         memcpy(bptr, "&lt;", 4);
2182                         bptr += 4;
2183                         Target->BufUsed += 4;
2184                 }
2185                 else if (*aptr == '>') {
2186                         memcpy(bptr, "&gt;", 4);
2187                         bptr += 4;
2188                         Target->BufUsed += 4;
2189                 }
2190                 else if (*aptr == '&') {
2191                         memcpy(bptr, "&amp;", 5);
2192                         bptr += 5;
2193                         Target->BufUsed += 5;
2194                 }
2195                 else if (*aptr == '"') {
2196                         memcpy(bptr, "&quot;", 6);
2197                         bptr += 6;
2198                         Target->BufUsed += 6;
2199                 }
2200                 else if (*aptr == '\'') {
2201                         memcpy(bptr, "&#39;", 5);
2202                         bptr += 5;
2203                         Target->BufUsed += 5;
2204                 }
2205                 else if (*aptr == LB) {
2206                         *bptr = '<';
2207                         bptr ++;
2208                         Target->BufUsed ++;
2209                 }
2210                 else if (*aptr == RB) {
2211                         *bptr = '>';
2212                         bptr ++;
2213                         Target->BufUsed ++;
2214                 }
2215                 else if (*aptr == QU) {
2216                         *bptr ='"';
2217                         bptr ++;
2218                         Target->BufUsed ++;
2219                 }
2220                 else if ((*aptr == 32) && (nbsp == 1)) {
2221                         memcpy(bptr, "&nbsp;", 6);
2222                         bptr += 6;
2223                         Target->BufUsed += 6;
2224                 }
2225                 else if ((*aptr == '\n') && (nolinebreaks == 1)) {
2226                         *bptr='\0';     /* nothing */
2227                 }
2228                 else if ((*aptr == '\n') && (nolinebreaks == 2)) {
2229                         memcpy(bptr, "&lt;br/&gt;", 11);
2230                         bptr += 11;
2231                         Target->BufUsed += 11;
2232                 }
2233
2234
2235                 else if ((*aptr == '\r') && (nolinebreaks != 0)) {
2236                         *bptr='\0';     /* nothing */
2237                 }
2238                 else{
2239                         *bptr = *aptr;
2240                         bptr++;
2241                         Target->BufUsed ++;
2242                 }
2243                 aptr ++;
2244         }
2245         *bptr = '\0';
2246         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2247                 return -1;
2248         return Target->BufUsed;
2249 }
2250
2251 /**
2252  * @ingroup StrBuf_DeEnCoder
2253  * @brief Append a string, escaping characters which have meaning in HTML.  
2254  * Converts linebreaks into blanks; escapes single quotes
2255  * @param Target        target buffer
2256  * @param Source        source buffer; set to NULL if you just have a C-String
2257  * @param PlainIn       Plain-C string to append; set to NULL if unused
2258  */
2259 void StrMsgEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2260 {
2261         const char *aptr, *eiptr;
2262         char *tptr, *eptr;
2263         long len;
2264
2265         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2266                 return ;
2267
2268         if (PlainIn != NULL) {
2269                 aptr = PlainIn;
2270                 len = strlen(PlainIn);
2271                 eiptr = aptr + len;
2272         }
2273         else {
2274                 aptr = Source->buf;
2275                 eiptr = aptr + Source->BufUsed;
2276                 len = Source->BufUsed;
2277         }
2278
2279         if (len == 0) 
2280                 return;
2281
2282         eptr = Target->buf + Target->BufSize - 8; 
2283         tptr = Target->buf + Target->BufUsed;
2284         
2285         while (aptr < eiptr){
2286                 if(tptr >= eptr) {
2287                         IncreaseBuf(Target, 1, -1);
2288                         eptr = Target->buf + Target->BufSize - 8; 
2289                         tptr = Target->buf + Target->BufUsed;
2290                 }
2291                
2292                 if (*aptr == '\n') {
2293                         *tptr = ' ';
2294                         Target->BufUsed++;
2295                 }
2296                 else if (*aptr == '\r') {
2297                         *tptr = ' ';
2298                         Target->BufUsed++;
2299                 }
2300                 else if (*aptr == '\'') {
2301                         *(tptr++) = '&';
2302                         *(tptr++) = '#';
2303                         *(tptr++) = '3';
2304                         *(tptr++) = '9';
2305                         *tptr = ';';
2306                         Target->BufUsed += 5;
2307                 } else {
2308                         *tptr = *aptr;
2309                         Target->BufUsed++;
2310                 }
2311                 tptr++; aptr++;
2312         }
2313         *tptr = '\0';
2314 }
2315
2316
2317
2318 /**
2319  * @ingroup StrBuf_DeEnCoder
2320  * @brief Append a string, escaping characters which have meaning in ICAL.  
2321  * [\n,] 
2322  * @param Target        target buffer
2323  * @param Source        source buffer; set to NULL if you just have a C-String
2324  * @param PlainIn       Plain-C string to append; set to NULL if unused
2325  */
2326 void StrIcalEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2327 {
2328         const char *aptr, *eiptr;
2329         char *tptr, *eptr;
2330         long len;
2331
2332         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2333                 return ;
2334
2335         if (PlainIn != NULL) {
2336                 aptr = PlainIn;
2337                 len = strlen(PlainIn);
2338                 eiptr = aptr + len;
2339         }
2340         else {
2341                 aptr = Source->buf;
2342                 eiptr = aptr + Source->BufUsed;
2343                 len = Source->BufUsed;
2344         }
2345
2346         if (len == 0) 
2347                 return;
2348
2349         eptr = Target->buf + Target->BufSize - 8; 
2350         tptr = Target->buf + Target->BufUsed;
2351         
2352         while (aptr < eiptr){
2353                 if(tptr + 3 >= eptr) {
2354                         IncreaseBuf(Target, 1, -1);
2355                         eptr = Target->buf + Target->BufSize - 8; 
2356                         tptr = Target->buf + Target->BufUsed;
2357                 }
2358                
2359                 if (*aptr == '\n') {
2360                         *tptr = '\\';
2361                         Target->BufUsed++;
2362                         tptr++;
2363                         *tptr = 'n';
2364                         Target->BufUsed++;
2365                 }
2366                 else if (*aptr == '\r') {
2367                         *tptr = '\\';
2368                         Target->BufUsed++;
2369                         tptr++;
2370                         *tptr = 'r';
2371                         Target->BufUsed++;
2372                 }
2373                 else if (*aptr == ',') {
2374                         *tptr = '\\';
2375                         Target->BufUsed++;
2376                         tptr++;
2377                         *tptr = ',';
2378                         Target->BufUsed++;
2379                 } else {
2380                         *tptr = *aptr;
2381                         Target->BufUsed++;
2382                 }
2383                 tptr++; aptr++;
2384         }
2385         *tptr = '\0';
2386 }
2387
2388 /**
2389  * @ingroup StrBuf_DeEnCoder
2390  * @brief Append a string, escaping characters which have meaning in JavaScript strings .  
2391  *
2392  * @param Target        target buffer
2393  * @param Source        source buffer; set to NULL if you just have a C-String
2394  * @param PlainIn       Plain-C string to append; set to NULL if unused
2395  * @returns size of result or -1
2396  */
2397 long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
2398 {
2399         const char *aptr, *eiptr;
2400         char *bptr, *eptr;
2401         long len;
2402         int IsUtf8Sequence;
2403
2404         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2405                 return -1;
2406
2407         if (PlainIn != NULL) {
2408                 aptr = PlainIn;
2409                 len = strlen(PlainIn);
2410                 eiptr = aptr + len;
2411         }
2412         else {
2413                 aptr = Source->buf;
2414                 eiptr = aptr + Source->BufUsed;
2415                 len = Source->BufUsed;
2416         }
2417
2418         if (len == 0) 
2419                 return -1;
2420
2421         bptr = Target->buf + Target->BufUsed;
2422         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2423
2424         while (aptr < eiptr){
2425                 if(bptr >= eptr) {
2426                         IncreaseBuf(Target, 1, -1);
2427                         eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in...  */
2428                         bptr = Target->buf + Target->BufUsed;
2429                 }
2430                 switch (*aptr) {
2431                 case '\n':
2432                         memcpy(bptr, HKEY("\\n"));
2433                         bptr += 2;
2434                         Target->BufUsed += 2;                           
2435                         break;
2436                 case '\r':
2437                         memcpy(bptr, HKEY("\\r"));
2438                         bptr += 2;
2439                         Target->BufUsed += 2;
2440                         break;
2441                 case '"':
2442                         *bptr = '\\';
2443                         bptr ++;
2444                         *bptr = '"';
2445                         bptr ++;
2446                         Target->BufUsed += 2;
2447                         break;
2448                 case '\\':
2449                         if ((*(aptr + 1) == 'u') &&
2450                             isxdigit(*(aptr + 2)) &&
2451                             isxdigit(*(aptr + 3)) &&
2452                             isxdigit(*(aptr + 4)) &&
2453                             isxdigit(*(aptr + 5)))
2454                         { /* oh, a unicode escaper. let it pass through. */
2455                                 memcpy(bptr, aptr, 6);
2456                                 aptr += 5;
2457                                 bptr +=6;
2458                                 Target->BufUsed += 6;
2459                         }
2460                         else 
2461                         {
2462                                 *bptr = '\\';
2463                                 bptr ++;
2464                                 *bptr = '\\';
2465                                 bptr ++;
2466                                 Target->BufUsed += 2;
2467                         }
2468                         break;
2469                 case '\b':
2470                         *bptr = '\\';
2471                         bptr ++;
2472                         *bptr = 'b';
2473                         bptr ++;
2474                         Target->BufUsed += 2;
2475                         break;
2476                 case '\f':
2477                         *bptr = '\\';
2478                         bptr ++;
2479                         *bptr = 'f';
2480                         bptr ++;
2481                         Target->BufUsed += 2;
2482                         break;
2483                 case '\t':
2484                         *bptr = '\\';
2485                         bptr ++;
2486                         *bptr = 't';
2487                         bptr ++;
2488                         Target->BufUsed += 2;
2489                         break;
2490                 default:
2491                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2492                         while (IsUtf8Sequence > 0){
2493                                 *bptr = *aptr;
2494                                 Target->BufUsed ++;
2495                                 if (--IsUtf8Sequence)
2496                                         aptr++;
2497                                 bptr++;
2498                         }
2499                 }
2500                 aptr ++;
2501         }
2502         *bptr = '\0';
2503         if ((bptr == eptr - 1 ) && !IsEmptyStr(aptr) )
2504                 return -1;
2505         return Target->BufUsed;
2506 }
2507
2508 /**
2509  * @ingroup StrBuf_DeEnCoder
2510  * @brief Append a string, escaping characters which have meaning in HTML + json.  
2511  *
2512  * @param Target        target buffer
2513  * @param Source        source buffer; set to NULL if you just have a C-String
2514  * @param PlainIn       Plain-C string to append; set to NULL if unused
2515  * @param nbsp          If nonzero, spaces are converted to non-breaking spaces.
2516  * @param nolinebreaks  if set to 1, linebreaks are removed from the string.
2517  *                      if set to 2, linebreaks are replaced by &ltbr/&gt
2518  */
2519 long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn, int nbsp, int nolinebreaks)
2520 {
2521         const char *aptr, *eiptr;
2522         char *bptr, *eptr;
2523         long len;
2524         int IsUtf8Sequence = 0;
2525
2526         if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
2527                 return -1;
2528
2529         if (PlainIn != NULL) {
2530                 aptr = PlainIn;
2531                 len = strlen(PlainIn);
2532                 eiptr = aptr + len;
2533         }
2534         else {
2535                 aptr = Source->buf;
2536                 eiptr = aptr + Source->BufUsed;
2537                 len = Source->BufUsed;
2538         }
2539
2540         if (len == 0) 
2541                 return -1;
2542
2543         bptr = Target->buf + Target->BufUsed;
2544         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2545
2546         while (aptr < eiptr){
2547                 if(bptr >= eptr) {
2548                         IncreaseBuf(Target, 1, -1);
2549                         eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in...  */
2550                         bptr = Target->buf + Target->BufUsed;
2551                 }
2552                 switch (*aptr) {
2553                 case '<':
2554                         memcpy(bptr, HKEY("&lt;"));
2555                         bptr += 4;
2556                         Target->BufUsed += 4;
2557                         break;
2558                 case '>':
2559                         memcpy(bptr, HKEY("&gt;"));
2560                         bptr += 4;
2561                         Target->BufUsed += 4;
2562                         break;
2563                 case '&':
2564                         memcpy(bptr, HKEY("&amp;"));
2565                         bptr += 5;
2566                         Target->BufUsed += 5;
2567                         break;
2568                 case LB:
2569                         *bptr = '<';
2570                         bptr ++;
2571                         Target->BufUsed ++;
2572                         break;
2573                 case RB:
2574                         *bptr = '>';
2575                         bptr ++;
2576                         Target->BufUsed ++;
2577                         break;
2578                 case '\n':
2579                         switch (nolinebreaks) {
2580                         case 1:
2581                                 *bptr='\0';     /* nothing */
2582                                 break;
2583                         case 2:
2584                                 memcpy(bptr, HKEY("&lt;br/&gt;"));
2585                                 bptr += 11;
2586                                 Target->BufUsed += 11;
2587                                 break;
2588                         default:
2589                                 memcpy(bptr, HKEY("\\n"));
2590                                 bptr += 2;
2591                                 Target->BufUsed += 2;                           
2592                         }
2593                         break;
2594                 case '\r':
2595                         switch (nolinebreaks) {
2596                         case 1:
2597                         case 2:
2598                                 *bptr='\0';     /* nothing */
2599                                 break;
2600                         default:
2601                                 memcpy(bptr, HKEY("\\r"));
2602                                 bptr += 2;
2603                                 Target->BufUsed += 2;
2604                                 break;
2605                         }
2606                         break;
2607                 case '"':
2608                 case QU:
2609                         *bptr = '\\';
2610                         bptr ++;
2611                         *bptr = '"';
2612                         bptr ++;
2613                         Target->BufUsed += 2;
2614                         break;
2615                 case '\\':
2616                         if ((*(aptr + 1) == 'u') &&
2617                             isxdigit(*(aptr + 2)) &&
2618                             isxdigit(*(aptr + 3)) &&
2619                             isxdigit(*(aptr + 4)) &&
2620                             isxdigit(*(aptr + 5)))
2621                         { /* oh, a unicode escaper. let it pass through. */
2622                                 memcpy(bptr, aptr, 6);
2623                                 aptr += 5;
2624                                 bptr +=6;
2625                                 Target->BufUsed += 6;
2626                         }
2627                         else 
2628                         {
2629                                 *bptr = '\\';
2630                                 bptr ++;
2631                                 *bptr = '\\';
2632                                 bptr ++;
2633                                 Target->BufUsed += 2;
2634                         }
2635                         break;
2636                 case '\b':
2637                         *bptr = '\\';
2638                         bptr ++;
2639                         *bptr = 'b';
2640                         bptr ++;
2641                         Target->BufUsed += 2;
2642                         break;
2643                 case '\f':
2644                         *bptr = '\\';
2645                         bptr ++;
2646                         *bptr = 'f';
2647                         bptr ++;
2648                         Target->BufUsed += 2;
2649                         break;
2650                 case '\t':
2651                         *bptr = '\\';
2652                         bptr ++;
2653                         *bptr = 't';
2654                         bptr ++;
2655                         Target->BufUsed += 2;
2656                         break;
2657                 case  32:
2658                         if (nbsp == 1) {
2659                                 memcpy(bptr, HKEY("&nbsp;"));
2660                                 bptr += 6;
2661                                 Target->BufUsed += 6;
2662                                 break;
2663                         }
2664                 default:
2665                         IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(aptr, eiptr);
2666                         while (IsUtf8Sequence > 0){
2667                                 *bptr = *aptr;
2668                                 Target->BufUsed ++;
2669                                 if (--IsUtf8Sequence)
2670                                         aptr++;
2671                                 bptr++;
2672                         }
2673                 }
2674                 aptr ++;
2675         }
2676         *bptr = '\0';
2677         if ((bptr = eptr - 1 ) && !IsEmptyStr(aptr) )
2678                 return -1;
2679         return Target->BufUsed;
2680 }
2681
2682
2683 /**
2684  * @ingroup StrBuf_DeEnCoder
2685  * @brief replace all non-Ascii characters by another
2686  * @param Buf buffer to inspect
2687  * @param repl charater to stamp over non ascii chars
2688  */
2689 void StrBufAsciify(StrBuf *Buf, const char repl)
2690 {
2691         long offset;
2692
2693         for (offset = 0; offset < Buf->BufUsed; offset ++)
2694                 if (!isascii(Buf->buf[offset]))
2695                         Buf->buf[offset] = repl;
2696         
2697 }
2698
2699 /**
2700  * @ingroup StrBuf_DeEnCoder
2701  * @brief unhide special chars hidden to the HTML escaper
2702  * @param target buffer to put the unescaped string in
2703  * @param source buffer to unescape
2704  */
2705 void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source) 
2706 {
2707         int a, b, len;
2708         char hex[3];
2709
2710         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2711         {
2712                 return;
2713         }
2714
2715         if (target != NULL)
2716                 FlushStrBuf(target);
2717
2718         len = source->BufUsed;
2719         for (a = 0; a < len; ++a) {
2720                 if (target->BufUsed >= target->BufSize)
2721                         IncreaseBuf(target, 1, -1);
2722
2723                 if (source->buf[a] == '=') {
2724                         hex[0] = source->buf[a + 1];
2725                         hex[1] = source->buf[a + 2];
2726                         hex[2] = 0;
2727                         b = 0;
2728                         sscanf(hex, "%02x", &b);
2729                         target->buf[target->BufUsed] = b;
2730                         target->buf[++target->BufUsed] = 0;
2731                         a += 2;
2732                 }
2733                 else {
2734                         target->buf[target->BufUsed] = source->buf[a];
2735                         target->buf[++target->BufUsed] = 0;
2736                 }
2737         }
2738 }
2739
2740
2741 /**
2742  * @ingroup StrBuf_DeEnCoder
2743  * @brief hide special chars from the HTML escapers and friends
2744  * @param target buffer to put the escaped string in
2745  * @param source buffer to escape
2746  */
2747 void StrBufEUid_escapize(StrBuf *target, const StrBuf *source) 
2748 {
2749         int i, len;
2750
2751         if (target != NULL)
2752                 FlushStrBuf(target);
2753
2754         if ((source == NULL) || (target == NULL) || (target->buf == NULL))
2755         {
2756                 return;
2757         }
2758
2759         len = source->BufUsed;
2760         for (i=0; i<len; ++i) {
2761                 if (target->BufUsed + 4 >= target->BufSize)
2762                         IncreaseBuf(target, 1, -1);
2763                 if ( (isalnum(source->buf[i])) || 
2764                      (source->buf[i]=='-') || 
2765                      (source->buf[i]=='_') ) {
2766                         target->buf[target->BufUsed++] = source->buf[i];
2767                 }
2768                 else {
2769                         sprintf(&target->buf[target->BufUsed], 
2770                                 "=%02X", 
2771                                 (0xFF &source->buf[i]));
2772                         target->BufUsed += 3;
2773                 }
2774         }
2775         target->buf[target->BufUsed + 1] = '\0';
2776 }
2777
2778
2779 /*******************************************************************************
2780  *                      Quoted Printable de/encoding                           *
2781  *******************************************************************************/
2782
2783 /**
2784  * @ingroup StrBuf_DeEnCoder
2785  * @brief decode a buffer from base 64 encoding; destroys original
2786  * @param Buf Buffor to transform
2787  */
2788 int StrBufDecodeBase64(StrBuf *Buf)
2789 {
2790         char *xferbuf;
2791         size_t siz;
2792
2793         if (Buf == NULL)
2794                 return -1;
2795
2796         xferbuf = (char*) malloc(Buf->BufSize);
2797         if (xferbuf == NULL)
2798                 return -1;
2799
2800         *xferbuf = '\0';
2801         siz = CtdlDecodeBase64(xferbuf,
2802                                Buf->buf,
2803                                Buf->BufUsed);
2804         free(Buf->buf);
2805         Buf->buf = xferbuf;
2806         Buf->BufUsed = siz;
2807
2808         Buf->buf[Buf->BufUsed] = '\0';
2809         return siz;
2810 }
2811
2812 /**
2813  * @ingroup StrBuf_DeEnCoder
2814  * @brief decode a buffer from base 64 encoding; expects targetbuffer
2815  * @param BufIn Buffor to transform
2816  * @param BufOut Buffer to put result into
2817  */
2818 int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
2819 {
2820         if ((BufIn == NULL) || (BufOut == NULL))
2821                 return -1;
2822
2823         if (BufOut->BufSize < BufIn->BufUsed)
2824                 IncreaseBuf(BufOut, 0, BufIn->BufUsed);
2825
2826         BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
2827                                            BufIn->buf,
2828                                            BufIn->BufUsed);
2829         return BufOut->BufUsed;
2830 }
2831
2832 typedef struct __z_enc_stream {
2833         StrBuf OutBuf;
2834         z_stream zstream;
2835 } z_enc_stream;
2836
2837 vStreamT *StrBufNewStreamContext(eStreamType type, const char **Err)
2838 {
2839         base64_decodestate *state;;
2840         *Err = NULL;
2841
2842         switch (type)
2843         {
2844         case eBase64Decode:
2845         case eBase64Encode:
2846                 state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
2847                 base64_init_decodestate(state);
2848                 return (vStreamT*) state;
2849                 break;
2850         case eZLibDecode:
2851         {
2852
2853                 z_enc_stream *stream;
2854                 int err;
2855
2856                 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2857                 memset(stream, 0, sizeof(z_enc_stream));
2858                 stream->OutBuf.BufSize = 4*SIZ; /// TODO 64
2859                 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2860
2861                 err = inflateInit(&stream->zstream);
2862
2863                 if (err != Z_OK) {
2864                         StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2865                         *Err = zError(err);
2866                         return NULL;
2867                 }
2868                 return (vStreamT*) stream;
2869
2870         }
2871         case eZLibEncode:
2872         {
2873                 z_enc_stream *stream;
2874                 int err;
2875
2876                 stream = (z_enc_stream *) malloc(sizeof(z_enc_stream));
2877                 memset(stream, 0, sizeof(z_enc_stream));
2878                 stream->OutBuf.BufSize = 4*SIZ; /// todo 64
2879                 stream->OutBuf.buf = (char*)malloc(stream->OutBuf.BufSize);
2880                 /* write gzip header */
2881                 stream->OutBuf.BufUsed = snprintf
2882                         (stream->OutBuf.buf,
2883                          stream->OutBuf.BufSize, 
2884                          "%c%c%c%c%c%c%c%c%c%c",
2885                          gz_magic[0], gz_magic[1], Z_DEFLATED,
2886                          0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
2887                          OS_CODE);
2888
2889                 err = deflateInit2(&stream->zstream,
2890                                    ZLibCompressionRatio,
2891                                    Z_DEFLATED,
2892                                    -MAX_WBITS,
2893                                    DEF_MEM_LEVEL,
2894                                    Z_DEFAULT_STRATEGY);
2895                 if (err != Z_OK) {
2896                         StrBufDestroyStreamContext(type, (vStreamT**) &stream, Err);
2897                         *Err = zError(err);
2898                         return NULL;
2899                 }
2900                 return (vStreamT*) stream;
2901         }
2902         case eEmtyCodec:
2903                 /// TODO
2904
2905                 break;
2906         }
2907         return NULL;
2908 }
2909
2910 int StrBufDestroyStreamContext(eStreamType type, vStreamT **vStream, const char **Err)
2911 {
2912         int err;
2913         int rc = 0;
2914         *Err = NULL;
2915         
2916         if ((vStream == NULL) || (*vStream==NULL)) {
2917                 *Err = strerror(EINVAL);
2918                 return EINVAL;
2919         }
2920         switch (type)
2921         {
2922         case eBase64Encode:
2923         case eBase64Decode:
2924                 free(*vStream);
2925                 *vStream = NULL;
2926                 break;
2927         case eZLibDecode:
2928         {
2929                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2930                 (void)inflateEnd(&stream->zstream);
2931                 free(stream->OutBuf.buf);
2932                 free(stream);
2933         }
2934         case eZLibEncode:
2935         {
2936                 z_enc_stream *stream = (z_enc_stream *)*vStream;
2937                 err = deflateEnd(&stream->zstream);
2938                 if (err != Z_OK) {
2939                         *Err = zError(err);
2940                         rc = -1;
2941                 }
2942                 free(stream->OutBuf.buf);
2943                 free(stream);
2944                 *vStream = NULL;
2945                 break;
2946         }
2947         case eEmtyCodec: 
2948                 break; /// TODO
2949         }
2950         return rc;
2951 }
2952
2953 int StrBufStreamTranscode(eStreamType type, IOBuffer *Target, IOBuffer *In, const char* pIn, long pInLen, vStreamT *vStream, int LastChunk, const char **Err)
2954 {
2955         int rc = 0;
2956         switch (type)
2957         {
2958         case eBase64Encode:
2959         {
2960                 /// TODO
2961 /*
2962                 // base64_decodestate *state = (base64_decodestate*)vStream;
2963                 long ExpectLen;
2964                 
2965                 if (In != NULL)
2966                 {
2967                         pIn = In->buf;
2968                         pInLen = In->BufUsed;
2969                 }
2970                 if ((In == NULL) || (vStream == NULL))
2971                         return;
2972                 
2973                 ExpectLen = (pInLen / 4) * 3;
2974
2975                 if (Target->BufSize - Target->BufUsed < ExpectLen)
2976                 {
2977                         IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
2978                 }
2979
2980                 ////    ExpectLen = base64_encode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
2981                 Target->BufUsed += ExpectLen;
2982                 Target->buf[Target->BufUsed] = '\0';
2983 */
2984         }
2985         break;
2986         case eBase64Decode:
2987         {
2988 /*
2989                 base64_decodestate *state = (base64_decodestate *)vStream;
2990                 long ExpectLen;
2991                 
2992                 if (In != NULL)
2993                 {
2994                         pIn = In->buf;
2995                         pInLen = In->BufUsed;
2996                 }
2997                 if ((pIn == NULL) || (vStream == NULL))
2998                         return;
2999                 
3000                 ExpectLen = (pInLen / 4) * 3;
3001
3002                 if (Target->BufSize - Target->BufUsed < ExpectLen)
3003                 {
3004                         IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
3005                 }
3006
3007                 ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
3008                 Target->BufUsed += ExpectLen;
3009                 Target->buf[Target->BufUsed] = '\0';
3010 */
3011         }
3012         break;
3013         case eZLibEncode:
3014         {
3015                 z_enc_stream *stream = (z_enc_stream *)vStream;
3016                 int org_outbuf_len = stream->OutBuf.BufUsed;
3017                 int err;
3018                 unsigned int chunkavail;
3019
3020                 if (In->ReadWritePointer != NULL)
3021                 {
3022                         stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3023                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
3024                                 (In->ReadWritePointer - In->Buf->buf);
3025                 }
3026                 else
3027                 {
3028                         stream->zstream.next_in = (Bytef *) In->Buf->buf;
3029                         stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3030                 }
3031                 
3032                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3033                 stream->zstream.avail_out = chunkavail = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3034
3035                 err = deflate(&stream->zstream,  (LastChunk) ? Z_FINISH : Z_NO_FLUSH);
3036
3037                 stream->OutBuf.BufUsed += (chunkavail - stream->zstream.avail_out);
3038
3039                 if (Target && 
3040                     (LastChunk ||
3041                      (stream->OutBuf.BufUsed != org_outbuf_len)
3042                             ))
3043                 {
3044                         iSwapBuffers(Target->Buf, &stream->OutBuf);
3045                 }
3046
3047                 if (stream->zstream.avail_in == 0)
3048                 {
3049                         FlushStrBuf(In->Buf);
3050                         In->ReadWritePointer = NULL;
3051                 }
3052                 else
3053                 {
3054                         if (stream->zstream.avail_in < 64)
3055                         {
3056                                 memmove(In->Buf->buf,
3057                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3058                                         stream->zstream.avail_in);
3059
3060                                 In->Buf->BufUsed = stream->zstream.avail_in;
3061                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
3062                         }
3063                         else
3064                         {
3065                                 
3066                                 In->ReadWritePointer = In->Buf->buf + 
3067                                         (In->Buf->BufUsed - stream->zstream.avail_in);
3068                         }
3069                 }
3070                 rc = (LastChunk && (err != Z_FINISH));
3071                 if (!rc && (err != Z_OK)) {
3072                         *Err = zError(err);
3073                 }
3074                 
3075         }
3076         break;
3077         case eZLibDecode: {
3078                 z_enc_stream *stream = (z_enc_stream *)vStream;
3079                 int org_outbuf_len = stream->zstream.total_out;
3080                 int err;
3081
3082                 if ((stream->zstream.avail_out != 0) && (stream->zstream.next_in != NULL)) {
3083                         if (In->ReadWritePointer != NULL)
3084                         {
3085                                 stream->zstream.next_in = (Bytef *) In->ReadWritePointer;
3086                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed - 
3087                                         (In->ReadWritePointer - In->Buf->buf);
3088                         }
3089                         else
3090                         {
3091                                 stream->zstream.next_in = (Bytef *) In->Buf->buf;
3092                                 stream->zstream.avail_in = (uInt) In->Buf->BufUsed;
3093                         }
3094                 }
3095
3096                 stream->zstream.next_out = (unsigned char*)stream->OutBuf.buf + stream->OutBuf.BufUsed;
3097                 stream->zstream.avail_out = (uInt) stream->OutBuf.BufSize - stream->OutBuf.BufUsed;
3098
3099                 err = inflate(&stream->zstream, Z_NO_FLUSH);
3100
3101                 ///assert(ret != Z_STREAM_ERROR);  /* state not clobbered * /
3102                 switch (err) {
3103                 case Z_NEED_DICT:
3104                         err = Z_DATA_ERROR;     /* and fall through */
3105
3106                 case Z_DATA_ERROR:
3107                         *Err = zError(err);
3108                 case Z_MEM_ERROR:
3109                         (void)inflateEnd(&stream->zstream);
3110                         return err;
3111                 }
3112
3113                 stream->OutBuf.BufUsed += stream->zstream.total_out + org_outbuf_len;
3114
3115                 if (Target) iSwapBuffers(Target->Buf, &stream->OutBuf);
3116
3117                 if (stream->zstream.avail_in == 0)
3118                 {
3119                         FlushStrBuf(In->Buf);
3120                         In->ReadWritePointer = NULL;
3121                 }
3122                 else
3123                 {
3124                         if (stream->zstream.avail_in < 64)
3125                         {
3126                                 memmove(In->Buf->buf,
3127                                         In->Buf->buf + In->Buf->BufUsed - stream->zstream.avail_in,
3128                                         stream->zstream.avail_in);
3129
3130                                 In->Buf->BufUsed = stream->zstream.avail_in;
3131                                 In->Buf->buf[In->Buf->BufUsed] = '\0';
3132                         }
3133                         else
3134                         {
3135                                 
3136                                 In->ReadWritePointer = In->Buf->buf + 
3137                                         (In->Buf->BufUsed - stream->zstream.avail_in);
3138                         }
3139                 }
3140         }
3141                 break;
3142         case eEmtyCodec: {
3143
3144         }
3145                 break; /// TODO
3146         }
3147         return rc;
3148 }
3149
3150 /**
3151  * @ingroup StrBuf_DeEnCoder
3152  * @brief decode a buffer from base 64 encoding; destroys original
3153  * @param Buf Buffor to transform
3154  */
3155 int StrBufDecodeHex(StrBuf *Buf)
3156 {
3157         unsigned int ch;
3158         char *pch, *pche, *pchi;
3159
3160         if (Buf == NULL) return -1;
3161
3162         pch = pchi = Buf->buf;
3163         pche = pch + Buf->BufUsed;
3164
3165         while (pchi < pche){
3166                 ch = decode_hex(pchi);
3167                 *pch = ch;
3168                 pch ++;
3169                 pchi += 2;
3170         }
3171
3172         *pch = '\0';
3173         Buf->BufUsed = pch - Buf->buf;
3174         return Buf->BufUsed;
3175 }
3176
3177 /**
3178  * @ingroup StrBuf_DeEnCoder
3179  * @brief replace all chars >0x20 && < 0x7F with Mute
3180  * @param Mute char to put over invalid chars
3181  * @param Buf Buffor to transform
3182  */
3183 int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
3184 {
3185         unsigned char *pch;
3186
3187         if (Buf == NULL) return -1;
3188         pch = (unsigned char *)Buf->buf;
3189         while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
3190                 if ((*pch < 0x20) || (*pch > 0x7F))
3191                         *pch = Mute;
3192                 pch ++;
3193         }
3194         return Buf->BufUsed;
3195 }
3196
3197
3198 /**
3199  * @ingroup StrBuf_DeEnCoder
3200  * @brief remove escaped strings from i.e. the url string (like %20 for blanks)
3201  * @param Buf Buffer to translate
3202  * @param StripBlanks Reduce several blanks to one?
3203  */
3204 long StrBufUnescape(StrBuf *Buf, int StripBlanks)
3205 {
3206         int a, b;
3207         char hex[3];
3208         long len;
3209
3210         if (Buf == NULL)
3211                 return -1;
3212
3213         while ((Buf->BufUsed > 0) && (isspace(Buf->buf[Buf->BufUsed - 1]))){
3214                 Buf->buf[Buf->BufUsed - 1] = '\0';
3215                 Buf->BufUsed --;
3216         }
3217
3218         a = 0; 
3219         while (a < Buf->BufUsed) {
3220                 if (Buf->buf[a] == '+')
3221                         Buf->buf[a] = ' ';
3222                 else if (Buf->buf[a] == '%') {
3223                         /* don't let % chars through, rather truncate the input. */
3224                         if (a + 2 > Buf->BufUsed) {
3225                                 Buf->buf[a] = '\0';
3226                                 Buf->BufUsed = a;
3227                         }
3228                         else {                  
3229                                 hex[0] = Buf->buf[a + 1];
3230                                 hex[1] = Buf->buf[a + 2];
3231                                 hex[2] = 0;
3232                                 b = 0;
3233                                 sscanf(hex, "%02x", &b);
3234                                 Buf->buf[a] = (char) b;
3235                                 len = Buf->BufUsed - a - 2;
3236                                 if (len > 0)
3237                                         memmove(&Buf->buf[a + 1], &Buf->buf[a + 3], len);
3238                         
3239                                 Buf->BufUsed -=2;
3240                         }
3241                 }
3242                 a++;
3243         }
3244         return a;
3245 }
3246
3247
3248 /**
3249  * @ingroup StrBuf_DeEnCoder
3250  * @brief       RFC2047-encode a header field if necessary.
3251  *              If no non-ASCII characters are found, the string
3252  *              will be copied verbatim without encoding.
3253  *
3254  * @param       target          Target buffer.
3255  * @param       source          Source string to be encoded.
3256  * @returns     encoded length; -1 if non success.
3257  */
3258 int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
3259 {
3260         const char headerStr[] = "=?UTF-8?Q?";
3261         int need_to_encode = 0;
3262         int i = 0;
3263         unsigned char ch;
3264
3265         if ((source == NULL) || 
3266             (target == NULL))
3267             return -1;
3268
3269         while ((i < source->BufUsed) &&
3270                (!IsEmptyStr (&source->buf[i])) &&
3271                (need_to_encode == 0)) {
3272                 if (((unsigned char) source->buf[i] < 32) || 
3273                     ((unsigned char) source->buf[i] > 126)) {
3274                         need_to_encode = 1;
3275                 }
3276                 i++;
3277         }
3278
3279         if (!need_to_encode) {
3280                 if (*target == NULL) {
3281                         *target = NewStrBufPlain(source->buf, source->BufUsed);
3282                 }
3283                 else {
3284                         FlushStrBuf(*target);
3285                         StrBufAppendBuf(*target, source, 0);
3286                 }
3287                 if (*target != 0)
3288                         return (*target)->BufUsed;
3289                 else
3290                         return 0;
3291         }
3292         if (*target == NULL)
3293                 *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
3294         else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
3295                 IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
3296         memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
3297         (*target)->BufUsed = sizeof(headerStr) - 1;
3298         for (i=0; (i < source->BufUsed); ++i) {
3299                 if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3300                         IncreaseBuf(*target, 1, 0);
3301                 ch = (unsigned char) source->buf[i];
3302                 if ((ch  <  32) || 
3303                     (ch  > 126) || 
3304                     (ch == '=') ||
3305                     (ch == '?') ||
3306                     (ch == '_') ||
3307                     (ch == '[') ||
3308                     (ch == ']')   )
3309                 {
3310                         sprintf(&(*target)->buf[(*target)->BufUsed], "=%02X", ch);
3311                         (*target)->BufUsed += 3;
3312                 }
3313                 else {
3314                         if (ch == ' ')
3315                                 (*target)->buf[(*target)->BufUsed] = '_';
3316                         else
3317                                 (*target)->buf[(*target)->BufUsed] = ch;
3318                         (*target)->BufUsed++;
3319                 }
3320         }
3321         
3322         if ((*target)->BufUsed + 4 >= (*target)->BufSize)
3323                 IncreaseBuf(*target, 1, 0);
3324
3325         (*target)->buf[(*target)->BufUsed++] = '?';
3326         (*target)->buf[(*target)->BufUsed++] = '=';
3327         (*target)->buf[(*target)->BufUsed] = '\0';
3328         return (*target)->BufUsed;;
3329 }
3330
3331 /**
3332  * @ingroup StrBuf_DeEnCoder
3333  * @brief       Quoted-Printable encode a message; make it < 80 columns width.
3334  * @param       source          Source string to be encoded.
3335  * @returns     buffer with encoded message.
3336  */
3337 StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
3338 {
3339         StrBuf *OutBuf;
3340         char *Optr, *OEptr;
3341         const char *ptr, *eptr;
3342         unsigned char ch;
3343         int LinePos;
3344
3345         OutBuf = NewStrBufPlain(NULL, StrLength(EncodeMe) * 4);
3346         Optr = OutBuf->buf;
3347         OEptr = OutBuf->buf + OutBuf->BufSize;
3348         ptr = EncodeMe->buf;
3349         eptr = EncodeMe->buf + EncodeMe->BufUsed;
3350         LinePos = 0;
3351
3352         while (ptr < eptr)
3353         {
3354                 if (Optr + 4 >= OEptr)
3355                 {
3356                         long Offset;
3357                         Offset = Optr - OutBuf->buf;
3358                         OutBuf->BufUsed = Optr - OutBuf->buf;
3359                         IncreaseBuf(OutBuf, 1, 0);
3360                         Optr = OutBuf->buf + Offset;
3361                         OEptr = OutBuf->buf + OutBuf->BufSize;
3362                 }
3363                 if (*ptr == '\r')
3364                 {
3365                         /* ignore carriage returns */
3366                         ptr ++;
3367                 }
3368                 else if (*ptr == '\n') {
3369                         /* hard line break */
3370                         memcpy(Optr, HKEY("=0A"));
3371                         Optr += 3;
3372                         LinePos += 3;
3373                         ptr ++;
3374                 }
3375                 else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
3376                          ( (*ptr >= 62) && (*ptr <= 126) ))
3377                 {
3378                         *Optr = *ptr;
3379                         Optr ++;
3380                         ptr ++;
3381                         LinePos ++;
3382                 }
3383                 else {
3384                         ch = *ptr;
3385                         *Optr = '=';
3386                         Optr ++;
3387                         *Optr = HexList[ch][0];
3388                         Optr ++;
3389                         *Optr = HexList[ch][1];
3390                         Optr ++;
3391                         LinePos += 3;
3392                         ptr ++;
3393                 }
3394
3395                 if (LinePos > 72) {
3396                         /* soft line break */
3397                         if (isspace(*(Optr - 1))) {
3398                                 ch = *(Optr - 1);
3399                                 Optr --;
3400                                 *Optr = '=';
3401                                 Optr ++;
3402                                 *Optr = HexList[ch][0];
3403                                 Optr ++;
3404                                 *Optr = HexList[ch][1];
3405                                 Optr ++;
3406                                 LinePos += 3;
3407                         }
3408                         *Optr = '=';
3409                         Optr ++;
3410                         *Optr = '\n';
3411                         Optr ++;
3412                         LinePos = 0;
3413                 }
3414         }
3415         *Optr = '\0';
3416         OutBuf->BufUsed = Optr - OutBuf->buf;
3417
3418         return OutBuf;
3419 }
3420
3421
3422 static void AddRecipient(StrBuf *Target, 
3423                          StrBuf *UserName, 
3424                          StrBuf *EmailAddress, 
3425                          StrBuf *EncBuf)
3426 {
3427         int QuoteMe = 0;
3428
3429         if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
3430         if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
3431
3432         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
3433         StrBufRFC2047encode(&EncBuf, UserName);
3434         StrBufAppendBuf(Target, EncBuf, 0);
3435         if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
3436         else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
3437
3438         if (StrLength(EmailAddress) > 0){
3439                 StrBufAppendBufPlain(Target, HKEY("<"), 0);
3440                 StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
3441                 StrBufAppendBufPlain(Target, HKEY(">"), 0);
3442         }
3443 }
3444
3445
3446 /**
3447  * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
3448  * \param Recp Source list of email recipients
3449  * \param UserName Temporary buffer for internal use; Please provide valid buffer.
3450  * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
3451  * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
3452  * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
3453  */
3454 StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
3455                                            StrBuf *UserName, 
3456                                            StrBuf *EmailAddress,
3457                                            StrBuf *EncBuf)
3458 {
3459         StrBuf *Target;
3460         const char *pch, *pche;
3461         const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
3462
3463         if ((Recp == NULL) || (StrLength(Recp) == 0))
3464                 return NULL;
3465
3466         pch = ChrPtr(Recp);
3467         pche = pch + StrLength(Recp);
3468
3469         if (!CheckEncode(pch, -1, pche))
3470                 return NewStrBufDup(Recp);
3471
3472         Target = NewStrBufPlain(NULL, StrLength(Recp));
3473
3474         while ((pch != NULL) && (pch < pche))
3475         {
3476                 while (isspace(*pch)) pch++;
3477                 UserEnd = EmailStart = EmailEnd = NULL;
3478                 
3479                 if ((*pch == '"') || (*pch == '\'')) {
3480                         UserStart = pch + 1;
3481                         
3482                         UserEnd = strchr(UserStart, *pch);
3483                         if (UserEnd == NULL) 
3484                                 break; ///TODO: Userfeedback??
3485                         EmailStart = UserEnd + 1;
3486                         while (isspace(*EmailStart))
3487                                 EmailStart++;
3488                         if (UserEnd == UserStart) {
3489                                 UserStart = UserEnd = NULL;
3490                         }
3491                         
3492                         if (*EmailStart == '<') {
3493                                 EmailStart++;
3494                                 EmailEnd = strchr(EmailStart, '>');
3495                                 if (EmailEnd == NULL)
3496                                         EmailEnd = strchr(EmailStart, ',');
3497                                 
3498                         }
3499                         else {
3500                                 EmailEnd = strchr(EmailStart, ',');
3501                         }
3502                         if (EmailEnd == NULL)
3503                                 EmailEnd = pche;
3504                         pch = EmailEnd + 1;
3505                 }
3506                 else {
3507                         int gt = 0;
3508                         UserStart = pch;
3509                         EmailEnd = strchr(UserStart, ',');
3510                         if (EmailEnd == NULL) {
3511                                 EmailEnd = strchr(pch, '>');
3512                                 pch = NULL;
3513                                 if (EmailEnd != NULL) {
3514                                         gt = 1;
3515                                 }
3516                                 else {
3517                                         EmailEnd = pche;
3518                                 }
3519                         }
3520                         else {
3521
3522                                 pch = EmailEnd + 1;
3523                                 while ((EmailEnd > UserStart) && !gt &&
3524                                        ((*EmailEnd == ',') ||
3525                                         (*EmailEnd == '>') ||
3526                                         (isspace(*EmailEnd))))
3527                                 {
3528                                         if (*EmailEnd == '>')
3529                                                 gt = 1;
3530                                         else 
3531                                                 EmailEnd--;
3532                                 }
3533                                 if (EmailEnd == UserStart)
3534                                         break;
3535                         }
3536                         if (gt) {
3537                                 EmailStart = strchr(UserStart, '<');
3538                                 if ((EmailStart == NULL) || (EmailStart > EmailEnd))
3539                                         break;
3540                                 UserEnd = EmailStart;
3541
3542                                 while ((UserEnd > UserStart) && 
3543                                        isspace (*(UserEnd - 1)))
3544                                         UserEnd --;
3545                                 EmailStart ++;
3546                                 if (UserStart >= UserEnd)
3547                                         UserStart = UserEnd = NULL;
3548                         }
3549                         else { /* this is a local recipient... no domain, just a realname */
3550                                 EmailStart = UserStart;
3551                                 At = strchr(EmailStart, '@');
3552                                 if (At == NULL) {
3553                                         UserEnd = EmailEnd;
3554                                         EmailEnd = NULL;
3555                                 }
3556                                 else {
3557                                         EmailStart = UserStart;
3558                                         UserStart = NULL;
3559                                 }
3560                         }
3561                 }
3562
3563                 if ((UserStart != NULL) && (UserEnd != NULL))
3564                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3565                 else if ((UserStart != NULL) && (UserEnd == NULL))
3566                         StrBufPlain(UserName, UserStart, UserEnd - UserStart);
3567                 else
3568                         FlushStrBuf(UserName);
3569
3570                 if ((EmailStart != NULL) && (EmailEnd != NULL))
3571                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
3572                 else if ((EmailStart != NULL) && (EmailEnd == NULL))
3573                         StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
3574                 else 
3575                         FlushStrBuf(EmailAddress);
3576
3577                 AddRecipient(Target, UserName, EmailAddress, EncBuf);
3578
3579                 if (pch == NULL)
3580                         break;
3581                 
3582                 if ((pch != NULL) && (*pch == ','))
3583                         pch ++;
3584                 if (pch != NULL) while (isspace(*pch))
3585                         pch ++;
3586         }
3587         return Target;
3588 }
3589
3590
3591 /**
3592  * @ingroup StrBuf
3593  * @brief replaces all occurances of 'search' by 'replace'
3594  * @param buf Buffer to modify
3595  * @param search character to search
3596  * @param replace character to replace search by
3597  */
3598 void StrBufReplaceChars(StrBuf *buf, char search, char replace)
3599 {
3600         long i;
3601         if (buf == NULL)
3602                 return;
3603         for (i=0; i<buf->BufUsed; i++)
3604                 if (buf->buf[i] == search)
3605                         buf->buf[i] = replace;
3606
3607 }
3608
3609 /**
3610  * @ingroup StrBuf
3611  * @brief removes all \\r s from the string, or replaces them with \n if its not a combination of both.
3612  * @param buf Buffer to modify
3613  */
3614 void StrBufToUnixLF(StrBuf *buf)
3615 {
3616         char *pche, *pchS, *pchT;
3617         if (buf == NULL)
3618                 return;
3619
3620         pche = buf->buf + buf->BufUsed;
3621         pchS = pchT = buf->buf;
3622         while (pchS < pche)
3623         {
3624                 if (*pchS == '\r')
3625                 {
3626                         pchS ++;
3627                         if (*pchS != '\n') {
3628                                 *pchT = '\n';
3629                                 pchT++;
3630                         }
3631                 }
3632                 *pchT = *pchS;
3633                 pchT++; pchS++;
3634         }
3635         *pchT = '\0';
3636         buf->BufUsed = pchT - buf->buf;
3637 }
3638
3639
3640 /*******************************************************************************
3641  *                 Iconv Wrapper; RFC822 de/encoding                           *
3642  *******************************************************************************/
3643
3644 /**
3645  * @ingroup StrBuf_DeEnCoder
3646  * @brief Wrapper around iconv_open()
3647  * Our version adds aliases for non-standard Microsoft charsets
3648  * such as 'MS950', aliasing them to names like 'CP950'
3649  *
3650  * @param tocode        Target encoding
3651  * @param fromcode      Source encoding
3652  * @param pic           anonimized pointer to iconv struct
3653  */
3654 void  ctdl_iconv_open(const char *tocode, const char *fromcode, void *pic)
3655 {
3656 #ifdef HAVE_ICONV
3657         iconv_t ic = (iconv_t)(-1) ;
3658         ic = iconv_open(tocode, fromcode);
3659         if (ic == (iconv_t)(-1) ) {
3660                 char alias_fromcode[64];
3661                 if ( (strlen(fromcode) == 5) && (!strncasecmp(fromcode, "MS", 2)) ) {
3662                         safestrncpy(alias_fromcode, fromcode, sizeof alias_fromcode);
3663                         alias_fromcode[0] = 'C';
3664                         alias_fromcode[1] = 'P';
3665                         ic = iconv_open(tocode, alias_fromcode);
3666                 }
3667         }
3668         *(iconv_t *)pic = ic;
3669 #endif
3670 }
3671
3672
3673 /**
3674  * @ingroup StrBuf_DeEnCoder
3675  * @brief find one chunk of a RFC822 encoded string
3676  * @param Buffer where to search
3677  * @param bptr where to start searching
3678  * @returns found position, NULL if none.
3679  */
3680 static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
3681 {
3682         const char * end;
3683         /* Find the next ?Q? */
3684         if (Buf->BufUsed - (bptr - Buf->buf)  < 6)
3685                 return NULL;
3686
3687         end = strchr(bptr + 2, '?');
3688
3689         if (end == NULL)
3690                 return NULL;
3691
3692         if ((Buf->BufUsed - (end - Buf->buf) > 3) &&
3693             (((*(end + 1) == 'B') || (*(end + 1) == 'Q')) ||
3694              ((*(end + 1) == 'b') || (*(end + 1) == 'q'))) && 
3695             (*(end + 2) == '?')) {
3696                 /* skip on to the end of the cluster, the next ?= */
3697                 end = strstr(end + 3, "?=");
3698         }
3699         else
3700                 /* sort of half valid encoding, try to find an end. */
3701                 end = strstr(bptr, "?=");
3702         return end;
3703 }
3704
3705
3706
3707 /**
3708  * @ingroup StrBuf_DeEnCoder
3709  * @brief convert one buffer according to the preselected iconv pointer PIC
3710  * @param ConvertBuf buffer we need to translate
3711  * @param TmpBuf To share a workbuffer over several iterations. prepare to have it filled with useless stuff afterwards.
3712  * @param pic Pointer to the iconv-session Object
3713  */
3714 void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
3715 {
3716 #ifdef HAVE_ICONV
3717         long trycount = 0;
3718         size_t siz;
3719         iconv_t ic;
3720         char *ibuf;                     /**< Buffer of characters to be converted */
3721         char *obuf;                     /**< Buffer for converted characters */
3722         size_t ibuflen;                 /**< Length of input buffer */
3723         size_t obuflen;                 /**< Length of output buffer */
3724
3725
3726         if ((ConvertBuf == NULL) || (TmpBuf == NULL))
3727                 return;
3728
3729         /* since we're converting to utf-8, one glyph may take up to 6 bytes */
3730         if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
3731                 IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
3732 TRYAGAIN:
3733         ic = *(iconv_t*)pic;
3734         ibuf = ConvertBuf->buf;
3735         ibuflen = ConvertBuf->BufUsed;
3736         obuf = TmpBuf->buf;
3737         obuflen = TmpBuf->BufSize;
3738         
3739         siz = iconv(ic, &ibuf, &ibuflen, &obuf, &obuflen);
3740
3741         if (siz < 0) {
3742                 if (errno == E2BIG) {
3743                         trycount ++;                    
3744                         IncreaseBuf(TmpBuf, 0, 0);
3745                         if (trycount < 5) 
3746                                 goto TRYAGAIN;
3747
3748                 }
3749                 else if (errno == EILSEQ){ 
3750                         /* hm, invalid utf8 sequence... what to do now? */
3751                         /* An invalid multibyte sequence has been encountered in the input */
3752                 }
3753                 else if (errno == EINVAL) {
3754                         /* An incomplete multibyte sequence has been encountered in the input. */
3755                 }
3756
3757                 FlushStrBuf(TmpBuf);
3758         }
3759         else {
3760                 TmpBuf->BufUsed = TmpBuf->BufSize - obuflen;
3761                 TmpBuf->buf[TmpBuf->BufUsed] = '\0';
3762                 
3763                 /* little card game: wheres the red lady? */
3764                 iSwapBuffers(ConvertBuf, TmpBuf);
3765                 FlushStrBuf(TmpBuf);
3766         }
3767 #endif
3768 }
3769
3770
3771 /**
3772  * @ingroup StrBuf_DeEnCoder
3773  * @brief catches one RFC822 encoded segment, and decodes it.
3774  * @param Target buffer to fill with result
3775  * @param DecodeMe buffer with stuff to process
3776  * @param SegmentStart points to our current segment in DecodeMe
3777  * @param SegmentEnd Points to the end of our current segment in DecodeMe
3778  * @param ConvertBuf Workbuffer shared between several iterations. Random content; needs to be valid
3779  * @param ConvertBuf2 Workbuffer shared between several iterations. Random content; needs to be valid
3780  * @param FoundCharset Characterset to default decoding to; if we find another we will overwrite it.
3781  */
3782 inline static void DecodeSegment(StrBuf *Target, 
3783                                  const StrBuf *DecodeMe, 
3784                                  const char *SegmentStart, 
3785                                  const char *SegmentEnd, 
3786                                  StrBuf *ConvertBuf,
3787                                  StrBuf *ConvertBuf2, 
3788                                  StrBuf *FoundCharset)
3789 {
3790         StrBuf StaticBuf;
3791         char charset[128];
3792         char encoding[16];
3793 #ifdef HAVE_ICONV
3794         iconv_t ic = (iconv_t)(-1);
3795 #else
3796         void *ic = NULL;
3797 #endif
3798         /* Now we handle foreign character sets properly encoded
3799          * in RFC2047 format.
3800          */
3801         StaticBuf.buf = (char*) SegmentStart; /*< it will just be read there... */
3802         StaticBuf.BufUsed = SegmentEnd - SegmentStart;
3803         StaticBuf.BufSize = DecodeMe->BufSize - (SegmentStart - DecodeMe->buf);
3804         extract_token(charset, SegmentStart, 1, '?', sizeof charset);
3805         if (FoundCharset != NULL) {
3806                 FlushStrBuf(FoundCharset);
3807                 StrBufAppendBufPlain(FoundCharset, charset, -1, 0);
3808         }
3809         extract_token(encoding, SegmentStart, 2, '?', sizeof encoding);
3810         StrBufExtract_token(ConvertBuf, &StaticBuf, 3, '?');
3811         
3812         *encoding = toupper(*encoding);
3813         if (*encoding == 'B') { /**< base64 */
3814                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3815                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3816                 ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
3817                                                         ConvertBuf->buf, 
3818                                                         ConvertBuf->BufUsed);
3819         }
3820         else if (*encoding == 'Q') {    /**< quoted-printable */
3821                 long pos;
3822                 
3823                 pos = 0;
3824                 while (pos < ConvertBuf->BufUsed)
3825                 {
3826                         if (ConvertBuf->buf[pos] == '_') 
3827                                 ConvertBuf->buf[pos] = ' ';
3828                         pos++;
3829                 }
3830                 
3831                 if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
3832                         IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
3833
3834                 ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
3835                         ConvertBuf2->buf, 
3836                         ConvertBuf->buf,
3837                         ConvertBuf->BufUsed);
3838         }
3839         else {
3840                 StrBufAppendBuf(ConvertBuf2, ConvertBuf, 0);
3841         }
3842 #ifdef HAVE_ICONV
3843         ctdl_iconv_open("UTF-8", charset, &ic);
3844         if (ic != (iconv_t)(-1) ) {             
3845 #endif
3846                 StrBufConvert(ConvertBuf2, ConvertBuf, &ic);
3847                 StrBufAppendBuf(Target, ConvertBuf2, 0);
3848 #ifdef HAVE_ICONV
3849                 iconv_close(ic);
3850         }
3851         else {
3852                 StrBufAppendBufPlain(Target, HKEY("(unreadable)"), 0);
3853         }
3854 #endif
3855 }
3856
3857 /**
3858  * @ingroup StrBuf_DeEnCoder
3859  * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
3860  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3861  * @param Target where to put the decoded string to 
3862  * @param DecodeMe buffer with encoded string
3863  * @param DefaultCharset if we don't find one, which should we use?
3864  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3865  *        put it here for later use where no string might be known.
3866  */
3867 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
3868 {
3869         StrBuf *ConvertBuf;
3870         StrBuf *ConvertBuf2;
3871         ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
3872         ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
3873         
3874         StrBuf_RFC822_2_Utf8(Target, 
3875                              DecodeMe, 
3876                              DefaultCharset, 
3877                              FoundCharset, 
3878                              ConvertBuf, 
3879                              ConvertBuf2);
3880         FreeStrBuf(&ConvertBuf);
3881         FreeStrBuf(&ConvertBuf2);
3882 }
3883
3884 /**
3885  * @ingroup StrBuf_DeEnCoder
3886  * @brief Handle subjects with RFC2047 encoding such as:
3887  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
3888  * @param Target where to put the decoded string to 
3889  * @param DecodeMe buffer with encoded string
3890  * @param DefaultCharset if we don't find one, which should we use?
3891  * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
3892  *        put it here for later use where no string might be known.
3893  * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
3894  * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
3895  */
3896 void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
3897                           const StrBuf *DecodeMe, 
3898                           const StrBuf* DefaultCharset, 
3899                           StrBuf *FoundCharset, 
3900                           StrBuf *ConvertBuf, 
3901                           StrBuf *ConvertBuf2)
3902 {
3903         StrBuf *DecodedInvalidBuf = NULL;
3904         const StrBuf *DecodeMee = DecodeMe;
3905         const char *start, *end, *next, *nextend, *ptr = NULL;
3906 #ifdef HAVE_ICONV
3907         iconv_t ic = (iconv_t)(-1) ;
3908 #endif
3909         const char *eptr;
3910         int passes = 0;
3911         int i;
3912         int illegal_non_rfc2047_encoding = 0;
3913
3914
3915         if (DecodeMe == NULL)
3916                 return;
3917         /* Sometimes, badly formed messages contain strings which were simply
3918          *  written out directly in some foreign character set instead of
3919          *  using RFC2047 encoding.  This is illegal but we will attempt to
3920          *  handle it anyway by converting from a user-specified default
3921          *  charset to UTF-8 if we see any nonprintable characters.
3922          */
3923         
3924         for (i=0; i<DecodeMe->BufUsed; ++i) {
3925                 if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
3926                         illegal_non_rfc2047_encoding = 1;
3927                         break;
3928                 }
3929         }
3930
3931         if ((illegal_non_rfc2047_encoding) &&
3932             (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
3933             (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
3934         {
3935 #ifdef HAVE_ICONV
3936                 ctdl_iconv_open("UTF-8", ChrPtr(DefaultCharset), &ic);
3937                 if (ic != (iconv_t)(-1) ) {
3938                         DecodedInvalidBuf = NewStrBufDup(DecodeMe);
3939                         StrBufConvert(DecodedInvalidBuf, ConvertBuf, &ic);///TODO: don't void const?
3940                         DecodeMee = DecodedInvalidBuf;
3941                         iconv_close(ic);
3942                 }
3943 #endif
3944         }
3945
3946         /* pre evaluate the first pair */
3947         end = NULL;
3948         start = strstr(DecodeMee->buf, "=?");
3949         eptr = DecodeMee->buf + DecodeMee->BufUsed;
3950         if (start != NULL) 
3951                 end = FindNextEnd (DecodeMee, start + 2);
3952         else {
3953                 StrBufAppendBuf(Target, DecodeMee, 0);
3954                 FreeStrBuf(&DecodedInvalidBuf);
3955                 return;
3956         }
3957
3958
3959         if (start != DecodeMee->buf) {
3960                 long nFront;
3961                 
3962                 nFront = start - DecodeMee->buf;
3963                 StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
3964         }
3965         /*
3966          * Since spammers will go to all sorts of absurd lengths to get their
3967          * messages through, there are LOTS of corrupt headers out there.
3968          * So, prevent a really badly formed RFC2047 header from throwing
3969          * this function into an infinite loop.
3970          */
3971         while ((start != NULL) && 
3972                (end != NULL) && 
3973                (start < eptr) && 
3974                (end < eptr) && 
3975                (passes < 20))
3976         {
3977                 passes++;
3978                 DecodeSegment(Target, 
3979                               DecodeMee, 
3980                               start, 
3981                               end, 
3982                               ConvertBuf,
3983                               ConvertBuf2,
3984                               FoundCharset);
3985                 
3986                 next = strstr(end, "=?");
3987                 nextend = NULL;
3988                 if ((next != NULL) && 
3989                     (next < eptr))
3990                         nextend = FindNextEnd(DecodeMee, next);
3991                 if (nextend == NULL)
3992                         next = NULL;
3993
3994                 /* did we find two partitions */
3995                 if ((next != NULL) && 
3996                     ((next - end) > 2))
3997                 {
3998                         ptr = end + 2;
3999                         while ((ptr < next) && 
4000                                (isspace(*ptr) ||
4001                                 (*ptr == '\r') ||
4002                                 (*ptr == '\n') || 
4003                                 (*ptr == '\t')))
4004                                 ptr ++;
4005                         /* 
4006                          * did we find a gab just filled with blanks?
4007                          * if not, copy its stuff over.
4008                          */
4009                         if (ptr != next)
4010                         {
4011                                 StrBufAppendBufPlain(Target, 
4012                                                      end + 2, 
4013                                                      next - end - 2,
4014                                                      0);
4015                         }
4016                 }
4017                 /* our next-pair is our new first pair now. */
4018                 ptr = end + 2;
4019                 start = next;
4020                 end = nextend;
4021         }
4022         end = ptr;
4023         nextend = DecodeMee->buf + DecodeMee->BufUsed;
4024         if ((end != NULL) && (end < nextend)) {
4025                 ptr = end;
4026                 while ( (ptr < nextend) &&
4027                         (isspace(*ptr) ||
4028                          (*ptr == '\r') ||
4029                          (*ptr == '\n') || 
4030                          (*ptr == '\t')))
4031                         ptr ++;
4032                 if (ptr < nextend)
4033                         StrBufAppendBufPlain(Target, end, nextend - end, 0);
4034         }
4035         FreeStrBuf(&DecodedInvalidBuf);
4036 }
4037
4038 /*******************************************************************************
4039  *                   Manipulating UTF-8 Strings                                *
4040  *******************************************************************************/
4041
4042 /**
4043  * @ingroup StrBuf
4044  * @brief evaluate the length of an utf8 special character sequence
4045  * @param Char the character to examine
4046  * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII.
4047  */
4048 static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
4049 {
4050         int n = 0;
4051         unsigned char test = (1<<7);
4052
4053         if ((*CharS & 0xC0) != 0xC0) 
4054                 return 1;
4055
4056         while ((n < 8) && 
4057                ((test & ((unsigned char)*CharS)) != 0)) 
4058         {
4059                 test = test >> 1;
4060                 n ++;
4061         }
4062         if ((n > 6) || ((CharE - CharS) < n))
4063                 n = 0;
4064         return n;
4065 }
4066
4067 /**
4068  * @ingroup StrBuf
4069  * @brief detect whether this char starts an utf-8 encoded char
4070  * @param Char character to inspect
4071  * @returns yes or no
4072  */
4073 static inline int Ctdl_IsUtf8SequenceStart(const char Char)
4074 {
4075 /** 11??.???? indicates an UTF8 Sequence. */
4076         return ((Char & 0xC0) == 0xC0);
4077 }
4078
4079 /**
4080  * @ingroup StrBuf
4081  * @brief measure the number of glyphs in an UTF8 string...
4082  * @param Buf string to measure
4083  * @returns the number of glyphs in Buf
4084  */
4085 long StrBuf_Utf8StrLen(StrBuf *Buf)
4086 {
4087         int n = 0;
4088         int m = 0;
4089         char *aptr, *eptr;
4090
4091         if ((Buf == NULL) || (Buf->BufUsed == 0))
4092                 return 0;
4093         aptr = Buf->buf;
4094         eptr = Buf->buf + Buf->BufUsed;
4095         while ((aptr < eptr) && (*aptr != '\0')) {
4096                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4097                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4098                         while ((aptr < eptr) && (*aptr++ != '\0')&& (m-- > 0) );
4099                         n ++;
4100                 }
4101                 else {
4102                         n++;
4103                         aptr++;
4104                 }
4105         }
4106         return n;
4107 }
4108
4109 /**
4110  * @ingroup StrBuf
4111  * @brief cuts a string after maxlen glyphs
4112  * @param Buf string to cut to maxlen glyphs
4113  * @param maxlen how long may the string become?
4114  * @returns current length of the string
4115  */
4116 long StrBuf_Utf8StrCut(StrBuf *Buf, int maxlen)
4117 {
4118         char *aptr, *eptr;
4119         int n = 0, m = 0;
4120
4121         aptr = Buf->buf;
4122         eptr = Buf->buf + Buf->BufUsed;
4123         while ((aptr < eptr) && (*aptr != '\0')) {
4124                 if (Ctdl_IsUtf8SequenceStart(*aptr)){
4125                         m = Ctdl_GetUtf8SequenceLength(aptr, eptr);
4126                         while ((*aptr++ != '\0') && (m-- > 0));
4127                         n ++;
4128                 }
4129                 else {
4130                         n++;
4131                         aptr++;
4132                 }
4133                 if (n >= maxlen) {
4134                         *aptr = '\0';
4135                         Buf->BufUsed = aptr - Buf->buf;
4136                         return Buf->BufUsed;
4137                 }
4138         }
4139         return Buf->BufUsed;
4140
4141 }
4142
4143
4144
4145
4146
4147 /*******************************************************************************
4148  *                               wrapping ZLib                                 *
4149  *******************************************************************************/
4150
4151 /**
4152  * @ingroup StrBuf_DeEnCoder
4153  * @brief uses the same calling syntax as compress2(), but it
4154  *   creates a stream compatible with HTTP "Content-encoding: gzip"
4155  * @param dest compressed buffer
4156  * @param destLen length of the compresed data 
4157  * @param source source to encode
4158  * @param sourceLen length of source to encode 
4159  * @param level compression level
4160  */
4161 #ifdef HAVE_ZLIB
4162 int ZEXPORT compress_gzip(Bytef * dest,
4163                           size_t * destLen,
4164                           const Bytef * source,
4165                           uLong sourceLen,     
4166                           int level)
4167 {
4168         /* write gzip header */
4169         snprintf((char *) dest, *destLen, 
4170                  "%c%c%c%c%c%c%c%c%c%c",
4171                  gz_magic[0], gz_magic[1], Z_DEFLATED,
4172                  0 /*flags */ , 0, 0, 0, 0 /*time */ , 0 /* xflags */ ,
4173                  OS_CODE);
4174
4175         /* normal deflate */
4176         z_stream stream;
4177         int err;
4178         stream.next_in = (Bytef *) source;
4179         stream.avail_in = (uInt) sourceLen;
4180         stream.next_out = dest + 10L;   // after header
4181         stream.avail_out = (uInt) * destLen;
4182         if ((uLong) stream.avail_out != *destLen)
4183                 return Z_BUF_ERROR;
4184
4185         stream.zalloc = (alloc_func) 0;
4186         stream.zfree = (free_func) 0;
4187         stream.opaque = (voidpf) 0;
4188
4189         err = deflateInit2(&stream, level, Z_DEFLATED, -MAX_WBITS,
4190                            DEF_MEM_LEVEL, Z_DEFAULT_STRATEGY);
4191         if (err != Z_OK)
4192                 return err;
4193
4194         err = deflate(&stream, Z_FINISH);
4195         if (err != Z_STREAM_END) {
4196                 deflateEnd(&stream);
4197                 return err == Z_OK ? Z_BUF_ERROR : err;
4198         }
4199         *destLen = stream.total_out + 10L;
4200
4201         /* write CRC and Length */
4202         uLong crc = crc32(0L, source, sourceLen);
4203         int n;
4204         for (n = 0; n < 4; ++n, ++*destLen) {
4205                 dest[*destLen] = (int) (crc & 0xff);
4206                 crc >>= 8;
4207         }
4208         uLong len = stream.total_in;
4209         for (n = 0; n < 4; ++n, ++*destLen) {
4210                 dest[*destLen] = (int) (len & 0xff);
4211                 len >>= 8;
4212         }
4213         err = deflateEnd(&stream);
4214         return err;
4215 }
4216 #endif
4217
4218
4219 /**
4220  * @ingroup StrBuf_DeEnCoder
4221  * @brief compress the buffer with gzip
4222  * Attention! If you feed this a Const String, you must maintain the uncompressed buffer yourself!
4223  * @param Buf buffer whose content is to be gzipped
4224  */
4225 int CompressBuffer(StrBuf *Buf)
4226 {
4227 #ifdef HAVE_ZLIB
4228         char *compressed_data = NULL;
4229         size_t compressed_len, bufsize;
4230         int i = 0;
4231
4232         bufsize = compressed_len = Buf->BufUsed +  (Buf->BufUsed / 100) + 100;
4233         compressed_data = malloc(compressed_len);
4234         
4235         if (compressed_data == NULL)
4236                 return -1;
4237         /* Flush some space after the used payload so valgrind shuts up... */
4238         while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4239                 Buf->buf[Buf->BufUsed + i++] = '\0';
4240         if (compress_gzip((Bytef *) compressed_data,
4241                           &compressed_len,
4242                           (Bytef *) Buf->buf,
4243                           (uLongf) Buf->BufUsed, Z_BEST_SPEED) == Z_OK) {
4244                 if (!Buf->ConstBuf)
4245                         free(Buf->buf);
4246                 Buf->buf = compressed_data;
4247                 Buf->BufUsed = compressed_len;
4248                 Buf->BufSize = bufsize;
4249                 /* Flush some space after the used payload so valgrind shuts up... */
4250                 i = 0;
4251                 while ((i < 10) && (Buf->BufUsed + i < Buf->BufSize))
4252                         Buf->buf[Buf->BufUsed + i++] = '\0';
4253                 return 1;
4254         } else {
4255                 free(compressed_data);
4256         }
4257 #endif  /* HAVE_ZLIB */
4258         return 0;
4259 }
4260
4261 /*******************************************************************************
4262  *           File I/O; Callbacks to libevent                                   *
4263  *******************************************************************************/
4264
4265 long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
4266 {
4267         long bufremain = 0;
4268         int n;
4269         
4270         if ((FB == NULL) || (FB->Buf == NULL))
4271                 return -1;
4272
4273         /*
4274          * check whether the read pointer is somewhere in a range 
4275          * where a cut left is inexpensive
4276          */
4277
4278         if (FB->ReadWritePointer != NULL)
4279         {
4280                 long already_read;
4281                 
4282                 already_read = FB->ReadWritePointer - FB->Buf->buf;
4283                 bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4284
4285                 if (already_read != 0) {
4286                         long unread;
4287                         
4288                         unread = FB->Buf->BufUsed - already_read;
4289
4290                         /* else nothing to compact... */
4291                         if (unread == 0) {
4292                                 FB->ReadWritePointer = FB->Buf->buf;
4293                                 bufremain = FB->Buf->BufSize;                   
4294                         }
4295                         else if ((unread < 64) || 
4296                                  (bufremain < already_read))
4297                         {
4298                                 /* 
4299                                  * if its just a tiny bit remaining, or we run out of space... 
4300                                  * lets tidy up.
4301                                  */
4302                                 FB->Buf->BufUsed = unread;
4303                                 if (unread < already_read)
4304                                         memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
4305                                 else
4306                                         memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
4307                                 FB->ReadWritePointer = FB->Buf->buf;
4308                                 bufremain = FB->Buf->BufSize - unread - 1;
4309                         }
4310                         else if (bufremain < (FB->Buf->BufSize / 10))
4311                         {
4312                                 /* get a bigger buffer */ 
4313
4314                                 IncreaseBuf(FB->Buf, 0, FB->Buf->BufUsed + 1);
4315
4316                                 FB->ReadWritePointer = FB->Buf->buf + unread;
4317
4318                                 bufremain = FB->Buf->BufSize - unread - 1;
4319 /*TODO: special increase function that won't copy the already read! */
4320                         }
4321                 }
4322                 else if (bufremain < 10) {
4323                         IncreaseBuf(FB->Buf, 1, FB->Buf->BufUsed + 10);
4324                         
4325                         FB->ReadWritePointer = FB->Buf->buf;
4326                         
4327                         bufremain = FB->Buf->BufSize - FB->Buf->BufUsed - 1;
4328                 }
4329                 
4330         }
4331         else {
4332                 FB->ReadWritePointer = FB->Buf->buf;
4333                 bufremain = FB->Buf->BufSize - 1;
4334         }
4335
4336         n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain);
4337
4338         if (n > 0) {
4339                 FB->Buf->BufUsed += n;
4340                 FB->Buf->buf[FB->Buf->BufUsed] = '\0';
4341         }
4342         return n;
4343 }
4344
4345 int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
4346 {
4347         long WriteRemain;
4348         int n;
4349
4350         if ((FB == NULL) || (FB->Buf == NULL))
4351                 return -1;
4352
4353         if (FB->ReadWritePointer != NULL)
4354         {
4355                 WriteRemain = FB->Buf->BufUsed - 
4356                         (FB->ReadWritePointer - 
4357                          FB->Buf->buf);
4358         }
4359         else {
4360                 FB->ReadWritePointer = FB->Buf->buf;
4361                 WriteRemain = FB->Buf->BufUsed;
4362         }
4363
4364         n = write(fd, FB->ReadWritePointer, WriteRemain);
4365         if (n > 0) {
4366                 FB->ReadWritePointer += n;
4367
4368                 if (FB->ReadWritePointer == 
4369                     FB->Buf->buf + FB->Buf->BufUsed)
4370                 {
4371                         FlushStrBuf(FB->Buf);
4372                         FB->ReadWritePointer = NULL;
4373                         return 0;
4374                 }
4375         // check whether we've got something to write
4376         // get the maximum chunk plus the pointer we can send
4377         // write whats there
4378         // if not all was sent, remember the send pointer for the next time
4379                 return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
4380         }
4381         return n;
4382 }
4383
4384 /**
4385  * @ingroup StrBuf_IO
4386  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
4387  * @param LineBuf your line will be copied here.
4388  * @param FB BLOB with lines of text...
4389  * @param Ptr moved arround to keep the next-line across several iterations
4390  *        has to be &NULL on start; will be &NotNULL on end of buffer
4391  * @returns size of copied buffer
4392  */
4393 eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
4394 {
4395         const char *aptr, *ptr, *eptr;
4396         char *optr, *xptr;
4397
4398         if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
4399                 return eReadFail;
4400         
4401
4402         if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
4403                 FB->ReadWritePointer = StrBufNOTNULL;
4404                 return eReadFail;
4405         }
4406
4407         FlushStrBuf(LineBuf);
4408         if (FB->ReadWritePointer == NULL)
4409                 ptr = aptr = FB->Buf->buf;
4410         else
4411                 ptr = aptr = FB->ReadWritePointer;
4412
4413         optr = LineBuf->buf;
4414         eptr = FB->Buf->buf + FB->Buf->BufUsed;
4415         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4416
4417         while ((ptr <= eptr) && 
4418                (*ptr != '\n') &&
4419                (*ptr != '\r') )
4420         {
4421                 *optr = *ptr;
4422                 optr++; ptr++;
4423                 if (optr == xptr) {
4424                         LineBuf->BufUsed = optr - LineBuf->buf;
4425                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
4426                         optr = LineBuf->buf + LineBuf->BufUsed;
4427                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
4428                 }
4429         }
4430
4431         if (ptr >= eptr) {
4432                 if (optr > LineBuf->buf)
4433                         optr --;
4434                 if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
4435                         LineBuf->BufUsed = optr - LineBuf->buf;
4436                         *optr = '\0';
4437                         if ((FB->ReadWritePointer != NULL) && 
4438                             (FB->ReadWritePointer != FB->Buf->buf))
4439                         {
4440                                 /* Ok, the client application read all the data 
4441                                    it was interested in so far. Since there is more to read, 
4442                                    we now shrink the buffer, and move the rest over.
4443                                 */
4444                                 StrBufCutLeft(FB->Buf, 
4445                                               FB->ReadWritePointer - FB->Buf->buf);
4446                                 FB->ReadWritePointer = FB->Buf->buf;
4447                         }
4448                         return eMustReadMore;
4449                 }
4450         }
4451         LineBuf->BufUsed = optr - LineBuf->buf;
4452         *optr = '\0';       
4453         if ((ptr <= eptr) && (*ptr == '\r'))
4454                 ptr ++;
4455         if ((ptr <= eptr) && (*ptr == '\n'))
4456                 ptr ++;
4457         
4458         if (ptr < eptr) {
4459                 FB->ReadWritePointer = ptr;
4460         }
4461         else {
4462                 FlushStrBuf(FB->Buf);
4463                 FB->ReadWritePointer = NULL;
4464         }
4465
4466         return eReadSuccess;
4467 }
4468
4469 /**
4470  * @ingroup StrBuf_CHUNKED_IO
4471  * @brief check whether the chunk-buffer has more data waiting or not.
4472  * @param FB Chunk-Buffer to inspect
4473  */
4474 eReadState StrBufCheckBuffer(IOBuffer *FB)
4475 {
4476         if (FB == NULL)
4477                 return eReadFail;
4478         if (FB->Buf->BufUsed == 0)
4479                 return eReadSuccess;
4480         if (FB->ReadWritePointer == NULL)
4481                 return eBufferNotEmpty;
4482         if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
4483                 return eBufferNotEmpty;
4484         return eReadSuccess;
4485 }
4486
4487 long IOBufferStrLength(IOBuffer *FB)
4488 {
4489         if ((FB == NULL) || (FB->Buf == NULL))
4490                 return 0;
4491         if (FB->ReadWritePointer == NULL)
4492                 return StrLength(FB->Buf);
4493         
4494         return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
4495 }
4496
4497
4498 /*******************************************************************************
4499  *           File I/O; Prefer buffered read since its faster!                  *
4500  *******************************************************************************/
4501
4502 /**
4503  * @ingroup StrBuf_IO
4504  * @brief Read a line from socket
4505  * flushes and closes the FD on error
4506  * @param buf the buffer to get the input to
4507  * @param fd pointer to the filedescriptor to read
4508  * @param append Append to an existing string or replace?
4509  * @param Error strerror() on error 
4510  * @returns numbers of chars read
4511  */
4512 int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
4513 {
4514         int len, rlen, slen;
4515
4516         if ((buf == NULL) || (buf->buf == NULL)) {
4517                 *Error = strerror(EINVAL);
4518                 return -1;
4519         }
4520
4521         if (!append)
4522                 FlushStrBuf(buf);
4523
4524         slen = len = buf->BufUsed;
4525         while (1) {
4526                 rlen = read(*fd, &buf->buf[len], 1);
4527                 if (rlen < 1) {
4528                         *Error = strerror(errno);
4529                         
4530                         close(*fd);
4531                         *fd = -1;
4532                         
4533                         return -1;
4534                 }
4535                 if (buf->buf[len] == '\n')
4536                         break;
4537                 if (buf->buf[len] != '\r')
4538                         len ++;
4539                 if (len + 2 >= buf->BufSize) {
4540                         buf->BufUsed = len;
4541                         buf->buf[len+1] = '\0';
4542                         IncreaseBuf(buf, 1, -1);
4543                 }
4544         }
4545         buf->BufUsed = len;
4546         buf->buf[len] = '\0';
4547         return len - slen;
4548 }
4549
4550
4551 /**
4552  * @ingroup StrBuf_BufferedIO
4553  * @brief Read a line from socket
4554  * flushes and closes the FD on error
4555  * @param Line the line to read from the fd / I/O Buffer
4556  * @param buf the buffer to get the input to
4557  * @param fd pointer to the filedescriptor to read
4558  * @param timeout number of successless selects until we bail out
4559  * @param selectresolution how long to wait on each select
4560  * @param Error strerror() on error 
4561  * @returns numbers of chars read
4562  */
4563 int StrBufTCP_read_buffered_line(StrBuf *Line, 
4564                                  StrBuf *buf, 
4565                                  int *fd, 
4566                                  int timeout, 
4567                                  int selectresolution, 
4568                                  const char **Error)
4569 {
4570         int len, rlen;
4571         int nSuccessLess = 0;
4572         fd_set rfds;
4573         char *pch = NULL;
4574         int fdflags;
4575         int IsNonBlock;
4576         struct timeval tv;
4577
4578         if (buf->BufUsed > 0) {
4579                 pch = strchr(buf->buf, '\n');
4580                 if (pch != NULL) {
4581                         rlen = 0;
4582                         len = pch - buf->buf;
4583                         if (len > 0 && (*(pch - 1) == '\r') )
4584                                 rlen ++;
4585                         StrBufSub(Line, buf, 0, len - rlen);
4586                         StrBufCutLeft(buf, len + 1);
4587                         return len - rlen;
4588                 }
4589         }
4590         
4591         if (buf->BufSize - buf->BufUsed < 10)
4592                 IncreaseBuf(buf, 1, -1);
4593
4594         fdflags = fcntl(*fd, F_GETFL);
4595         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4596
4597         while ((nSuccessLess < timeout) && (pch == NULL)) {
4598                 if (IsNonBlock){
4599                         tv.tv_sec = selectresolution;
4600                         tv.tv_usec = 0;
4601                         
4602                         FD_ZERO(&rfds);
4603                         FD_SET(*fd, &rfds);
4604                         if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
4605                                 *Error = strerror(errno);
4606                                 close (*fd);
4607                                 *fd = -1;
4608                                 return -1;
4609                         }
4610                 }
4611                 if (IsNonBlock && !  FD_ISSET(*fd, &rfds)) {
4612                         nSuccessLess ++;
4613                         continue;
4614                 }
4615                 rlen = read(*fd, 
4616                             &buf->buf[buf->BufUsed], 
4617                             buf->BufSize - buf->BufUsed - 1);
4618                 if (rlen < 1) {
4619                         *Error = strerror(errno);
4620                         close(*fd);
4621                         *fd = -1;
4622                         return -1;
4623                 }
4624                 else if (rlen > 0) {
4625                         nSuccessLess = 0;
4626                         buf->BufUsed += rlen;
4627                         buf->buf[buf->BufUsed] = '\0';
4628                         pch = strchr(buf->buf, '\n');
4629                         if ((pch == NULL) &&
4630                             (buf->BufUsed + 10 > buf->BufSize) &&
4631                             (IncreaseBuf(buf, 1, -1) == -1))
4632                                 return -1;
4633                         continue;
4634                 }
4635                 
4636         }
4637         if (pch != NULL) {
4638                 rlen = 0;
4639                 len = pch - buf->buf;
4640                 if (len > 0 && (*(pch - 1) == '\r') )
4641                         rlen ++;
4642                 StrBufSub(Line, buf, 0, len - rlen);
4643                 StrBufCutLeft(buf, len + 1);
4644                 return len - rlen;
4645         }
4646         return -1;
4647
4648 }
4649
4650 static const char *ErrRBLF_PreConditionFailed="StrBufTCP_read_buffered_line_fast: Wrong arguments or invalid Filedescriptor";
4651 static const char *ErrRBLF_SelectFailed="StrBufTCP_read_buffered_line_fast: Select failed without reason";
4652 static const char *ErrRBLF_NotEnoughSentFromServer="StrBufTCP_read_buffered_line_fast: No complete line was sent from peer";
4653 /**
4654  * @ingroup StrBuf_BufferedIO
4655  * @brief Read a line from socket
4656  * flushes and closes the FD on error
4657  * @param Line where to append our Line read from the fd / I/O Buffer; 
4658  * @param IOBuf the buffer to get the input to; lifetime pair to FD
4659  * @param Pos pointer to the current read position, should be NULL initialized on opening the FD it belongs to.!
4660  * @param fd pointer to the filedescriptor to read
4661  * @param timeout number of successless selects until we bail out
4662  * @param selectresolution how long to wait on each select
4663  * @param Error strerror() on error 
4664  * @returns numbers of chars read or -1 in case of error. "\n" will become 0
4665  */
4666 int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
4667                                       StrBuf *IOBuf, 
4668                                       const char **Pos,
4669                                       int *fd, 
4670                                       int timeout, 
4671                                       int selectresolution, 
4672                                       const char **Error)
4673 {
4674         const char *pche = NULL;
4675         const char *pos = NULL;
4676         const char *pLF;
4677         int len, rlen, retlen;
4678         int nSuccessLess = 0;
4679         fd_set rfds;
4680         const char *pch = NULL;
4681         int fdflags;
4682         int IsNonBlock;
4683         struct timeval tv;
4684         
4685         retlen = 0;
4686         if ((Line == NULL) ||
4687             (Pos == NULL) ||
4688             (IOBuf == NULL) ||
4689             (*fd == -1))
4690         {
4691                 if (Pos != NULL)
4692                         *Pos = NULL;
4693                 *Error = ErrRBLF_PreConditionFailed;
4694                 return -1;
4695         }
4696
4697         pos = *Pos;
4698         if ((IOBuf->BufUsed > 0) && 
4699             (pos != NULL) && 
4700             (pos < IOBuf->buf + IOBuf->BufUsed)) 
4701         {
4702                 char *pcht;
4703
4704                 pche = IOBuf->buf + IOBuf->BufUsed;
4705                 pch = pos;
4706                 pcht = Line->buf;
4707
4708                 while ((pch < pche) && (*pch != '\n'))
4709                 {
4710                         if (Line->BufUsed + 10 > Line->BufSize)
4711                         {
4712                                 long apos;
4713                                 apos = pcht - Line->buf;
4714                                 *pcht = '\0';
4715                                 IncreaseBuf(Line, 1, -1);
4716                                 pcht = Line->buf + apos;
4717                         }
4718                         *pcht++ = *pch++;
4719                         Line->BufUsed++;
4720                         retlen++;
4721                 }
4722
4723                 len = pch - pos;
4724                 if (len > 0 && (*(pch - 1) == '\r') )
4725                 {
4726                         retlen--;
4727                         len --;
4728                         pcht --;
4729                         Line->BufUsed --;
4730                 }
4731                 *pcht = '\0';
4732
4733                 if ((pch >= pche) || (*pch == '\0'))
4734                 {
4735                         FlushStrBuf(IOBuf);
4736                         *Pos = NULL;
4737                         pch = NULL;
4738                         pos = 0;
4739                 }
4740
4741                 if ((pch != NULL) && 
4742                     (pch <= pche)) 
4743                 {
4744                         if (pch + 1 >= pche) {
4745                                 *Pos = NULL;
4746                                 FlushStrBuf(IOBuf);
4747                         }
4748                         else
4749                                 *Pos = pch + 1;
4750                         
4751                         return retlen;
4752                 }
4753                 else 
4754                         FlushStrBuf(IOBuf);
4755         }
4756
4757         /* If we come here, Pos is Unset since we read everything into Line, and now go for more. */
4758         
4759         if (IOBuf->BufSize - IOBuf->BufUsed < 10)
4760                 IncreaseBuf(IOBuf, 1, -1);
4761
4762         fdflags = fcntl(*fd, F_GETFL);
4763         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4764
4765         pLF = NULL;
4766         while ((nSuccessLess < timeout) && 
4767                (pLF == NULL) &&
4768                (*fd != -1)) {
4769                 if (IsNonBlock)
4770                 {
4771                         tv.tv_sec = 1;
4772                         tv.tv_usec = 0;
4773                 
4774                         FD_ZERO(&rfds);
4775                         FD_SET(*fd, &rfds);
4776                         if (select((*fd) + 1, &rfds, NULL, NULL, &tv) == -1) {
4777                                 *Error = strerror(errno);
4778                                 close (*fd);
4779                                 *fd = -1;
4780                                 if (*Error == NULL)
4781                                         *Error = ErrRBLF_SelectFailed;
4782                                 return -1;
4783                         }
4784                         if (! FD_ISSET(*fd, &rfds) != 0) {
4785                                 nSuccessLess ++;
4786                                 continue;
4787                         }
4788                 }
4789                 rlen = read(*fd, 
4790                             &IOBuf->buf[IOBuf->BufUsed], 
4791                             IOBuf->BufSize - IOBuf->BufUsed - 1);
4792                 if (rlen < 1) {
4793                         *Error = strerror(errno);
4794                         close(*fd);
4795                         *fd = -1;
4796                         return -1;
4797                 }
4798                 else if (rlen > 0) {
4799                         nSuccessLess = 0;
4800                         pLF = IOBuf->buf + IOBuf->BufUsed;
4801                         IOBuf->BufUsed += rlen;
4802                         IOBuf->buf[IOBuf->BufUsed] = '\0';
4803                         
4804                         pche = IOBuf->buf + IOBuf->BufUsed;
4805                         
4806                         while ((pLF < pche) && (*pLF != '\n'))
4807                                 pLF ++;
4808                         if ((pLF >= pche) || (*pLF == '\0'))
4809                                 pLF = NULL;
4810
4811                         if (IOBuf->BufUsed + 10 > IOBuf->BufSize)
4812                         {
4813                                 long apos = 0;
4814
4815                                 if (pLF != NULL) apos = pLF - IOBuf->buf;
4816                                 IncreaseBuf(IOBuf, 1, -1);      
4817                                 if (pLF != NULL) pLF = IOBuf->buf + apos;
4818                         }
4819
4820                         continue;
4821                 }
4822                 else
4823                 {
4824                         nSuccessLess++;
4825                 }
4826         }
4827         *Pos = NULL;
4828         if (pLF != NULL) {
4829                 pos = IOBuf->buf;
4830                 len = pLF - pos;
4831                 if (len > 0 && (*(pLF - 1) == '\r') )
4832                         len --;
4833                 StrBufAppendBufPlain(Line, ChrPtr(IOBuf), len, 0);
4834                 if (pLF + 1 >= IOBuf->buf + IOBuf->BufUsed)
4835                 {
4836                         FlushStrBuf(IOBuf);
4837                 }
4838                 else 
4839                         *Pos = pLF + 1;
4840                 return retlen + len;
4841         }
4842         *Error = ErrRBLF_NotEnoughSentFromServer;
4843         return -1;
4844
4845 }
4846
4847 static const char *ErrRBLF_BLOBPreConditionFailed="StrBufReadBLOB: Wrong arguments or invalid Filedescriptor";
4848 /**
4849  * @ingroup StrBuf_IO
4850  * @brief Input binary data from socket
4851  * flushes and closes the FD on error
4852  * @param Buf the buffer to get the input to
4853  * @param fd pointer to the filedescriptor to read
4854  * @param append Append to an existing string or replace?
4855  * @param nBytes the maximal number of bytes to read
4856  * @param Error strerror() on error 
4857  * @returns numbers of chars read
4858  */
4859 int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **Error)
4860 {
4861         int fdflags;
4862         int rlen;
4863         int nSuccessLess;
4864         int nRead = 0;
4865         char *ptr;
4866         int IsNonBlock;
4867         struct timeval tv;
4868         fd_set rfds;
4869
4870         if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
4871         {
4872                 *Error = ErrRBLF_BLOBPreConditionFailed;
4873                 return -1;
4874         }
4875         if (!append)
4876                 FlushStrBuf(Buf);
4877         if (Buf->BufUsed + nBytes >= Buf->BufSize)
4878                 IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
4879
4880         ptr = Buf->buf + Buf->BufUsed;
4881
4882         fdflags = fcntl(*fd, F_GETFL);
4883         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
4884         nSuccessLess = 0;
4885         while ((nRead < nBytes) && 
4886                (*fd != -1)) 
4887         {
4888                 if (IsNonBlock)
4889                 {
4890                         tv.tv_sec = 1;
4891                         tv.tv_usec = 0;
4892                 
4893                         FD_ZERO(&rfds);
4894                         FD_SET(*fd, &rfds);
4895                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
4896                                 *Error = strerror(errno);
4897                                 close (*fd);
4898                                 *fd = -1;
4899                                 if (*Error == NULL)
4900                                         *Error = ErrRBLF_SelectFailed;
4901                                 return -1;
4902                         }
4903                         if (! FD_ISSET(*fd, &rfds) != 0) {
4904                                 nSuccessLess ++;
4905                                 continue;
4906                         }
4907                 }
4908
4909                 if ((rlen = read(*fd, 
4910                                  ptr,
4911                                  nBytes - nRead)) == -1) {
4912                         close(*fd);
4913                         *fd = -1;
4914                         *Error = strerror(errno);
4915                         return rlen;
4916                 }
4917                 nRead += rlen;
4918                 ptr += rlen;
4919                 Buf->BufUsed += rlen;
4920         }
4921         Buf->buf[Buf->BufUsed] = '\0';
4922         return nRead;
4923 }
4924
4925 const char *ErrRBB_BLOBFPreConditionFailed = "StrBufReadBLOBBuffered: to many selects; aborting.";
4926 const char *ErrRBB_too_many_selects        = "StrBufReadBLOBBuffered: to many selects; aborting.";
4927 /**
4928  * @ingroup StrBuf_BufferedIO
4929  * @brief Input binary data from socket
4930  * flushes and closes the FD on error
4931  * @param Blob put binary thing here
4932  * @param IOBuf the buffer to get the input to
4933  * @param Pos offset inside of IOBuf
4934  * @param fd pointer to the filedescriptor to read
4935  * @param append Append to an existing string or replace?
4936  * @param nBytes the maximal number of bytes to read
4937  * @param check whether we should search for '000\n' terminators in case of timeouts
4938  * @param Error strerror() on error 
4939  * @returns numbers of chars read
4940  */
4941 int StrBufReadBLOBBuffered(StrBuf *Blob, 
4942                            StrBuf *IOBuf, 
4943                            const char **Pos,
4944                            int *fd, 
4945                            int append, 
4946                            long nBytes, 
4947                            int check, 
4948                            const char **Error)
4949 {
4950         const char *pos;
4951         int fdflags;
4952         int rlen = 0;
4953         int nRead = 0;
4954         int nAlreadyRead = 0;
4955         int IsNonBlock;
4956         char *ptr;
4957         fd_set rfds;
4958         struct timeval tv;
4959         int nSuccessLess = 0;
4960         int MaxTries;
4961
4962         if ((Blob == NULL)  ||
4963             (*fd == -1)     ||
4964             (IOBuf == NULL) ||
4965             (Pos == NULL))
4966         {
4967                 if (Pos != NULL)
4968                         *Pos = NULL;
4969                 *Error = ErrRBB_BLOBFPreConditionFailed;
4970                 return -1;
4971         }
4972
4973         if (!append)
4974                 FlushStrBuf(Blob);
4975         if (Blob->BufUsed + nBytes >= Blob->BufSize) 
4976                 IncreaseBuf(Blob, append, Blob->BufUsed + nBytes);
4977         
4978         pos = *Pos;
4979
4980         if (pos != NULL) {
4981                 rlen = pos - IOBuf->buf;
4982         }
4983         rlen = IOBuf->BufUsed - rlen;
4984
4985
4986         if ((IOBuf->BufUsed > 0) && (pos != NULL) && (pos < IOBuf->buf + IOBuf->BufUsed)) 
4987         {
4988                 if (rlen < nBytes) {
4989                         memcpy(Blob->buf + Blob->BufUsed, pos, rlen);
4990                         Blob->BufUsed += rlen;
4991                         Blob->buf[Blob->BufUsed] = '\0';
4992                         nAlreadyRead = nRead = rlen;
4993                         *Pos = NULL; 
4994                 }
4995                 if (rlen >= nBytes) {
4996                         memcpy(Blob->buf + Blob->BufUsed, pos, nBytes);
4997                         Blob->BufUsed += nBytes;
4998                         Blob->buf[Blob->BufUsed] = '\0';
4999                         if (rlen == nBytes) {
5000                                 *Pos = NULL; 
5001                                 FlushStrBuf(IOBuf);
5002                         }
5003                         else 
5004                                 *Pos += nBytes;
5005                         return nBytes;
5006                 }
5007         }
5008
5009         FlushStrBuf(IOBuf);
5010         *Pos = NULL;
5011         if (IOBuf->BufSize < nBytes - nRead) {
5012                 IncreaseBuf(IOBuf, 0, nBytes - nRead);
5013         }
5014         ptr = IOBuf->buf;
5015
5016         fdflags = fcntl(*fd, F_GETFL);
5017         IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
5018         if (IsNonBlock)
5019                 MaxTries =   1000;
5020         else
5021                 MaxTries = 100000;
5022
5023         nBytes -= nRead;
5024         nRead = 0;
5025         while ((nSuccessLess < MaxTries) && (nRead < nBytes) && (*fd != -1)) {
5026                 if (IsNonBlock) {
5027                         tv.tv_sec = 1;
5028                         tv.tv_usec = 0;
5029                 
5030                         FD_ZERO(&rfds);
5031                         FD_SET(*fd, &rfds);
5032                         if (select(*fd + 1, &rfds, NULL, NULL, &tv) == -1) {
5033                                 *Error = strerror(errno);
5034                                 close (*fd);
5035                                 *fd = -1;
5036                                 if (*Error == NULL) {
5037                                         *Error = ErrRBLF_SelectFailed;
5038                                 }
5039                                 return -1;
5040                         }
5041                         if (! FD_ISSET(*fd, &rfds) != 0) {
5042                                 nSuccessLess ++;
5043                                 continue;
5044                         }
5045                 }
5046                 rlen = read(*fd, ptr, IOBuf->BufSize - (ptr - IOBuf->buf));
5047                 // if (rlen == -1) {            2021feb27 ajc changed this, apparently we will always get at least 1 byte unless the connection is broken
5048                 if (rlen < 1) {
5049                         close(*fd);
5050                         *fd = -1;
5051                         *Error = strerror(errno);
5052                         return rlen;
5053                 }
5054                 else if (rlen == 0){
5055                         if ((check == NNN_TERM) && (nRead > 5) && (strncmp(IOBuf->buf + IOBuf->BufUsed - 5, "\n000\n", 5) == 0)) {
5056                                 StrBufPlain(Blob, HKEY("\n000\n"));
5057                                 StrBufCutRight(Blob, 5);
5058                                 return Blob->BufUsed;
5059                         }
5060                         else if (!IsNonBlock) 
5061                                 nSuccessLess ++;
5062                         else if (nSuccessLess > MaxTries) {
5063                                 FlushStrBuf(IOBuf);
5064                                 *Error = ErrRBB_too_many_selects;
5065                                 return -1;
5066                         }
5067                 }
5068                 else if (rlen > 0) {
5069                         nSuccessLess = 0;
5070                         nRead += rlen;
5071                         ptr += rlen;
5072                         IOBuf->BufUsed += rlen;
5073                 }
5074         }
5075         if (nSuccessLess >= MaxTries) {
5076                 FlushStrBuf(IOBuf);
5077                 *Error = ErrRBB_too_many_selects;
5078                 return -1;
5079         }
5080
5081         if (nRead > nBytes) {
5082                 *Pos = IOBuf->buf + nBytes;
5083         }
5084         Blob->buf[Blob->BufUsed] = '\0';
5085         StrBufAppendBufPlain(Blob, IOBuf->buf, nBytes, 0);
5086         if (*Pos == NULL) {
5087                 FlushStrBuf(IOBuf);
5088         }
5089         return nRead + nAlreadyRead;
5090 }
5091
5092 /**
5093  * @ingroup StrBuf_IO
5094  * @brief extract a "next line" from Buf; Ptr to persist across several iterations
5095  * @param LineBuf your line will be copied here.
5096  * @param Buf BLOB with lines of text...
5097  * @param Ptr moved arround to keep the next-line across several iterations
5098  *        has to be &NULL on start; will be &NotNULL on end of buffer
5099  * @returns size of remaining buffer
5100  */
5101 int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
5102 {
5103         const char *aptr, *ptr, *eptr;
5104         char *optr, *xptr;
5105
5106         if ((Buf == NULL) ||
5107             (*Ptr == StrBufNOTNULL) ||
5108             (LineBuf == NULL)||
5109             (LineBuf->buf == NULL))
5110         {
5111                 *Ptr = StrBufNOTNULL;
5112                 return 0;
5113         }
5114
5115         FlushStrBuf(LineBuf);
5116         if (*Ptr==NULL)
5117                 ptr = aptr = Buf->buf;
5118         else
5119                 ptr = aptr = *Ptr;
5120
5121         optr = LineBuf->buf;
5122         eptr = Buf->buf + Buf->BufUsed;
5123         xptr = LineBuf->buf + LineBuf->BufSize - 1;
5124
5125         while ((ptr <= eptr) && 
5126                (*ptr != '\n') &&
5127                (*ptr != '\r') )
5128         {
5129                 *optr = *ptr;
5130                 optr++; ptr++;
5131                 if (optr == xptr) {
5132                         LineBuf->BufUsed = optr - LineBuf->buf;
5133                         IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
5134                         optr = LineBuf->buf + LineBuf->BufUsed;
5135                         xptr = LineBuf->buf + LineBuf->BufSize - 1;
5136                 }
5137         }
5138
5139         if ((ptr >= eptr) && (optr > LineBuf->buf))
5140                 optr --;
5141         LineBuf->BufUsed = optr - LineBuf->buf;
5142         *optr = '\0';       
5143         if ((ptr <= eptr) && (*ptr == '\r'))
5144                 ptr ++;
5145         if ((ptr <= eptr) && (*ptr == '\n'))
5146                 ptr ++;
5147         
5148         if (ptr < eptr) {
5149                 *Ptr = ptr;
5150         }
5151         else {
5152                 *Ptr = StrBufNOTNULL;
5153         }
5154
5155         return Buf->BufUsed - (ptr - Buf->buf);
5156 }
5157
5158
5159 /**
5160  * @ingroup StrBuf_IO
5161  * @brief removes double slashes from pathnames
5162  * @param Dir directory string to filter
5163  * @param RemoveTrailingSlash allows / disallows trailing slashes
5164  */
5165 void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
5166 {
5167         char *a, *b;
5168
5169         a = b = Dir->buf;
5170
5171         while (!IsEmptyStr(a)) {
5172                 if (*a == '/') {
5173                         while (*a == '/')
5174                                 a++;
5175                         *b = '/';
5176                         b++;
5177                 }
5178                 else {
5179                         *b = *a;
5180                         b++; a++;
5181                 }
5182         }
5183         if ((RemoveTrailingSlash) &&
5184             (b > Dir->buf) && 
5185             (*(b - 1) == '/')){
5186                 b--;
5187         }
5188         *b = '\0';
5189         Dir->BufUsed = b - Dir->buf;
5190 }
5191
5192
5193 /*
5194  * Decode a quoted-printable encoded StrBuf buffer "in place"
5195  * This is possible because the decoded will always be shorter than the encoded
5196  * so we don't have to worry about the buffer being to small.
5197  */
5198 void StrBufDecodeQP(StrBuf *Buf)
5199 {
5200         if (!Buf) {                             // sanity check #1
5201                 return;
5202         }
5203
5204         int source_len = StrLength(Buf);
5205         if (source_len < 1) {                   // sanity check #2
5206                 return;
5207         }
5208
5209         int spos = 0;                           // source position
5210         int tpos = 0;                           // target position
5211
5212         while (spos < source_len) {
5213                 if (!strncmp(&Buf->buf[spos], "=\r\n", 3)) {
5214                         spos += 3;
5215                 }
5216                 else if (!strncmp(&Buf->buf[spos], "=\n", 2)) {
5217                         spos += 2;
5218                 }
5219                 else if (Buf->buf[spos] == '=') {
5220                         ++spos;
5221                         int ch;
5222                         sscanf(&Buf->buf[spos], "%02x", &ch);
5223                         Buf->buf[tpos++] = ch;
5224                         spos +=2;
5225                 }
5226                 else {
5227                         Buf->buf[tpos++] = Buf->buf[spos++];
5228                 }
5229         }
5230
5231         Buf->buf[tpos] = 0;
5232         Buf->BufUsed = tpos;
5233 }