fix buffer overrun while converting charsets
[citadel.git] / libcitadel / lib / stringbuf.c
index 861c1d2f991b10e06c42a4b17bf0b43fae67db83..88cea7d704681a9490a25264a2b7aa40883c4d84 100644 (file)
@@ -215,6 +215,23 @@ void dbg_Init(StrBuf *Buf)
 
 #endif
 
+/**
+ * @ingroup StrBuf
+ * @brief swaps the contents of two StrBufs
+ * this is to be used to have cheap switched between a work-buffer and a target buffer 
+ * @param A First one
+ * @param B second one
+ */
+static inline void SwapBuffers(StrBuf *A, StrBuf *B)
+{
+       StrBuf C;
+
+       memcpy(&C, A, sizeof(*A));
+       memcpy(A, B, sizeof(*B));
+       memcpy(B, &C, sizeof(C));
+
+}
+
 /** 
  * @ingroup StrBuf_Cast
  * @brief Cast operator to Plain String 
@@ -372,6 +389,75 @@ StrBuf* NewStrBufDup(const StrBuf *CopyMe)
        return NewBuf;
 }
 
+/** 
+ * @ingroup StrBuf_DeConstructors
+ * @brief Copy Constructor; CreateRelpaceMe will contain CopyFlushMe afterwards.
+ * @param NoMe if non-NULL, we will use that buffer as value; KeepOriginal will abused as len.
+ * @param CopyFlushMe Buffer to faxmilate if KeepOriginal, or to move into CreateRelpaceMe if !KeepOriginal.
+ * @param CreateRelpaceMe If NULL, will be created, else Flushed and filled CopyFlushMe 
+ * @param KeepOriginal should CopyFlushMe remain intact? or may we Steal its buffer?
+ * @returns the new stringbuffer
+ */
+void NewStrBufDupAppendFlush(StrBuf **CreateRelpaceMe, StrBuf *CopyFlushMe, const char *NoMe, int KeepOriginal)
+{
+       StrBuf *NewBuf;
+       
+       if (CreateRelpaceMe == NULL)
+               return;
+
+       if (NoMe != NULL)
+       {
+               if (*CreateRelpaceMe != NULL)
+                       StrBufPlain(*CreateRelpaceMe, NoMe, KeepOriginal);
+               else 
+                       *CreateRelpaceMe = NewStrBufPlain(NoMe, KeepOriginal);
+               return;
+       }
+
+       if (CopyFlushMe == NULL)
+       {
+               if (*CreateRelpaceMe != NULL)
+                       FlushStrBuf(*CreateRelpaceMe);
+               else 
+                       *CreateRelpaceMe = NewStrBuf();
+               return;
+       }
+
+       /* 
+        * Randomly Chosen: bigger than 64 chars is cheaper to swap the buffers instead of copying.
+        * else *CreateRelpaceMe may use more memory than needed in a longer term, CopyFlushMe might
+        * be a big IO-Buffer...
+        */
+       if (KeepOriginal || (StrLength(CopyFlushMe) < 256))
+       {
+               if (*CreateRelpaceMe == NULL)
+               {
+                       *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
+                       dbg_Init(NewBuf);
+               }
+               else 
+               {
+                       NewBuf = *CreateRelpaceMe;
+                       FlushStrBuf(NewBuf);
+               }
+               StrBufAppendBuf(NewBuf, CopyFlushMe, 0);
+       }
+       else
+       {
+               if (*CreateRelpaceMe == NULL)
+               {
+                       *CreateRelpaceMe = NewBuf = NewStrBufPlain(NULL, CopyFlushMe->BufUsed);
+                       dbg_Init(NewBuf);
+               }
+               else 
+                       NewBuf = *CreateRelpaceMe;
+               SwapBuffers (NewBuf, CopyFlushMe);
+       }
+       if (!KeepOriginal)
+               FlushStrBuf(CopyFlushMe);
+       return;
+}
+
 /**
  * @ingroup StrBuf_DeConstructors
  * @brief create a new Buffer using an existing c-string
@@ -397,6 +483,11 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
                Siz *= 2;
 
        NewBuf->buf = (char*) malloc(Siz);
+       if (NewBuf->buf == NULL)
+       {
+               free(NewBuf);
+               return NULL;
+       }
        NewBuf->BufSize = Siz;
        if (ptr != NULL) {
                memcpy(NewBuf->buf, ptr, CopySize);
@@ -409,9 +500,9 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
        }
        NewBuf->ConstBuf = 0;
 
-       dbg_Init(NewBuf)
+       dbg_Init(NewBuf);
 
-               return NewBuf;
+       return NewBuf;
 }
 
 /**
@@ -985,6 +1076,26 @@ void StrBufTrim(StrBuf *Buf)
        }
        if (delta > 0) StrBufCutLeft(Buf, delta);
 }
+/**
+ * @ingroup StrBuf
+ * @brief changes all spaces in the string  (tab, linefeed...) to Blank (0x20)
+ * @param Buf the string to modify
+ */
+void StrBufSpaceToBlank(StrBuf *Buf)
+{
+       char *pche, *pch;
+
+       if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
+
+       pch = Buf->buf;
+       pche = pch + Buf->BufUsed;
+       while (pch < pche) 
+       {
+               if (isspace(*pch))
+                       *pch = ' ';
+               pch ++;
+       }
+}
 
 void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
 {
@@ -2259,6 +2370,7 @@ int StrBufDecodeBase64(StrBuf *Buf)
        if (Buf == NULL) return -1;
 
        xferbuf = (char*) malloc(Buf->BufSize);
+       *xferbuf = '\0';
        siz = CtdlDecodeBase64(xferbuf,
                               Buf->buf,
                               Buf->BufUsed);
@@ -2537,7 +2649,6 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
                                pch = NULL;
                                if (EmailEnd != NULL) {
                                        gt = 1;
-                                       EmailEnd --;
                                }
                                else {
                                        EmailEnd = pche;
@@ -2546,14 +2657,15 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
                        else {
 
                                pch = EmailEnd + 1;
-                               while ((EmailEnd > UserStart) && 
+                               while ((EmailEnd > UserStart) && !gt &&
                                       ((*EmailEnd == ',') ||
                                        (*EmailEnd == '>') ||
                                        (isspace(*EmailEnd))))
                                {
                                        if (*EmailEnd == '>')
                                                gt = 1;
-                                       EmailEnd--;
+                                       else 
+                                               EmailEnd--;
                                }
                                if (EmailEnd == UserStart)
                                        break;
@@ -2562,7 +2674,11 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
                                EmailStart = strchr(UserStart, '<');
                                if ((EmailStart == NULL) || (EmailStart > EmailEnd))
                                        break;
-                               UserEnd = EmailStart - 1;
+                               UserEnd = EmailStart;
+
+                               while ((UserEnd > UserStart) && 
+                                      isspace (*(UserEnd - 1)))
+                                       UserEnd --;
                                EmailStart ++;
                                if (UserStart >= UserEnd)
                                        UserStart = UserEnd = NULL;
@@ -2582,19 +2698,24 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
                        }
                }
 
-
-               if (UserStart != NULL)
+               if ((UserStart != NULL) && (UserEnd != NULL))
+                       StrBufPlain(UserName, UserStart, UserEnd - UserStart);
+               else if ((UserStart != NULL) && (UserEnd == NULL))
                        StrBufPlain(UserName, UserStart, UserEnd - UserStart);
                else
                        FlushStrBuf(UserName);
-               if (EmailStart != NULL)
+
+               if ((EmailStart != NULL) && (EmailEnd != NULL))
                        StrBufPlain(EmailAddress, EmailStart, EmailEnd - EmailStart);
+               else if ((EmailStart != NULL) && (EmailEnd == NULL))
+                       StrBufPlain(EmailAddress, EmailStart, EmailEnd - pche);
                else 
                        FlushStrBuf(EmailAddress);
 
                AddRecipient(Target, UserName, EmailAddress, EncBuf);
 
-
+               if (pch == NULL)
+                       break;
                
                if ((pch != NULL) && (*pch == ','))
                        pch ++;
@@ -2718,22 +2839,6 @@ static inline const char *FindNextEnd (const StrBuf *Buf, const char *bptr)
        return end;
 }
 
-/**
- * @ingroup StrBuf
- * @brief swaps the contents of two StrBufs
- * this is to be used to have cheap switched between a work-buffer and a target buffer 
- * @param A First one
- * @param B second one
- */
-static inline void SwapBuffers(StrBuf *A, StrBuf *B)
-{
-       StrBuf C;
-
-       memcpy(&C, A, sizeof(*A));
-       memcpy(A, B, sizeof(*B));
-       memcpy(B, &C, sizeof(C));
-
-}
 
 
 /**
@@ -2840,6 +2945,8 @@ inline static void DecodeSegment(StrBuf *Target,
        
        *encoding = toupper(*encoding);
        if (*encoding == 'B') { /**< base64 */
+               if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
+                       IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
                ConvertBuf2->BufUsed = CtdlDecodeBase64(ConvertBuf2->buf, 
                                                        ConvertBuf->buf, 
                                                        ConvertBuf->BufUsed);
@@ -2855,6 +2962,9 @@ inline static void DecodeSegment(StrBuf *Target,
                        pos++;
                }
                
+               if (ConvertBuf2->BufSize < ConvertBuf->BufUsed)
+                       IncreaseBuf(ConvertBuf2, 0, ConvertBuf->BufUsed);
+
                ConvertBuf2->BufUsed = CtdlDecodeQuotedPrintable(
                        ConvertBuf2->buf, 
                        ConvertBuf->buf,
@@ -2880,7 +2990,7 @@ inline static void DecodeSegment(StrBuf *Target,
 
 /**
  * @ingroup StrBuf_DeEnCoder
- * @brief Handle subjects with RFC2047 encoding such as:
+ * @brief Handle subjects with RFC2047 encoding such as: [deprecated old syntax!]
  * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
  * @param Target where to put the decoded string to 
  * @param DecodeMe buffer with encoded string
@@ -2889,9 +2999,42 @@ inline static void DecodeSegment(StrBuf *Target,
  *        put it here for later use where no string might be known.
  */
 void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf* DefaultCharset, StrBuf *FoundCharset)
+{
+       StrBuf *ConvertBuf;
+       StrBuf *ConvertBuf2;
+       ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
+       ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMe));
+       
+       StrBuf_RFC822_2_Utf8(Target, 
+                            DecodeMe, 
+                            DefaultCharset, 
+                            FoundCharset, 
+                            ConvertBuf, 
+                            ConvertBuf2);
+       FreeStrBuf(&ConvertBuf);
+       FreeStrBuf(&ConvertBuf2);
+}
+
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief Handle subjects with RFC2047 encoding such as:
+ * =?koi8-r?B?78bP0s3Mxc7JxSDXz9rE1dvO2c3JINvB0sHNySDP?=
+ * @param Target where to put the decoded string to 
+ * @param DecodeMe buffer with encoded string
+ * @param DefaultCharset if we don't find one, which should we use?
+ * @param FoundCharset overrides DefaultCharset if non-empty; If we find a charset inside of the string, 
+ *        put it here for later use where no string might be known.
+ * @param ConvertBuf workbuffer. feed in, you shouldn't care about its content.
+ * @param ConvertBuf2 workbuffer. feed in, you shouldn't care about its content.
+ */
+void StrBuf_RFC822_2_Utf8(StrBuf *Target, 
+                         const StrBuf *DecodeMe, 
+                         const StrBuf* DefaultCharset, 
+                         StrBuf *FoundCharset, 
+                         StrBuf *ConvertBuf, 
+                         StrBuf *ConvertBuf2)
 {
        StrBuf *DecodedInvalidBuf = NULL;
-       StrBuf *ConvertBuf, *ConvertBuf2;
        const StrBuf *DecodeMee = DecodeMe;
        const char *start, *end, *next, *nextend, *ptr = NULL;
 #ifdef HAVE_ICONV
@@ -2917,7 +3060,6 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf*
                }
        }
 
-       ConvertBuf = NewStrBufPlain(NULL, StrLength(DecodeMe));
        if ((illegal_non_rfc2047_encoding) &&
            (strcasecmp(ChrPtr(DefaultCharset), "UTF-8")) && 
            (strcasecmp(ChrPtr(DefaultCharset), "us-ascii")) )
@@ -2942,12 +3084,10 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf*
                end = FindNextEnd (DecodeMee, start);
        else {
                StrBufAppendBuf(Target, DecodeMee, 0);
-               FreeStrBuf(&ConvertBuf);
                FreeStrBuf(&DecodedInvalidBuf);
                return;
        }
 
-       ConvertBuf2 = NewStrBufPlain(NULL, StrLength(DecodeMee));
 
        if (start != DecodeMee->buf) {
                long nFront;
@@ -3026,8 +3166,6 @@ void StrBuf_RFC822_to_Utf8(StrBuf *Target, const StrBuf *DecodeMe, const StrBuf*
                if (ptr < nextend)
                        StrBufAppendBufPlain(Target, end, nextend - end, 0);
        }
-       FreeStrBuf(&ConvertBuf);
-       FreeStrBuf(&ConvertBuf2);
        FreeStrBuf(&DecodedInvalidBuf);
 }