]> code.citadel.org Git - citadel.git/blobdiff - libcitadel/lib/stringbuf.c
fix buffer overrun while converting charsets
[citadel.git] / libcitadel / lib / stringbuf.c
index 97be33b15d06fb58a5d43001dcbeabf2555f40d6..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 
@@ -286,14 +303,16 @@ static int IncreaseBuf(StrBuf *Buf, int KeepOriginal, int DestSize)
 
 /**
  * @ingroup StrBuf_DeConstructors
- * @brief shrink an _EMPTY_ buffer if its Buffer superseeds threshhold to NewSize. Buffercontent is thoroughly ignored and flushed.
+ * @brief shrink / increase an _EMPTY_ buffer to NewSize. Buffercontent is thoroughly ignored and flushed.
  * @param Buf Buffer to shrink (has to be empty)
  * @param ThreshHold if the buffer is bigger then this, its readjusted
  * @param NewSize if we Shrink it, how big are we going to be afterwards?
  */
 void ReAdjustEmptyBuf(StrBuf *Buf, long ThreshHold, long NewSize)
 {
-       if ((Buf != NULL) && (Buf->BufUsed > ThreshHold)) {
+       if ((Buf != NULL) && 
+           (Buf->BufUsed == 0) &&
+           (Buf->BufSize < ThreshHold)) {
                free(Buf->buf);
                Buf->buf = (char*) malloc(NewSize);
                Buf->BufUsed = 0;
@@ -370,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
@@ -395,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);
@@ -407,9 +500,9 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
        }
        NewBuf->ConstBuf = 0;
 
-       dbg_Init(NewBuf)
+       dbg_Init(NewBuf);
 
-               return NewBuf;
+       return NewBuf;
 }
 
 /**
@@ -983,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)
 {
@@ -2257,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);
@@ -2434,6 +2548,184 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
        return (*target)->BufUsed;;
 }
 
+
+
+static void AddRecipient(StrBuf *Target, 
+                        StrBuf *UserName, 
+                        StrBuf *EmailAddress, 
+                        StrBuf *EncBuf)
+{
+       int QuoteMe = 0;
+
+       if (StrLength(Target) > 0) StrBufAppendBufPlain(Target, HKEY(", "), 0);
+       if (strchr(ChrPtr(UserName), ',') != NULL) QuoteMe = 1;
+
+       if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\""), 0);
+       StrBufRFC2047encode(&EncBuf, UserName);
+       StrBufAppendBuf(Target, EncBuf, 0);
+       if (QuoteMe)  StrBufAppendBufPlain(Target, HKEY("\" "), 0);
+       else          StrBufAppendBufPlain(Target, HKEY(" "), 0);
+
+       if (StrLength(EmailAddress) > 0){
+               StrBufAppendBufPlain(Target, HKEY("<"), 0);
+               StrBufAppendBuf(Target, EmailAddress, 0); /* TODO: what about IDN???? */
+               StrBufAppendBufPlain(Target, HKEY(">"), 0);
+       }
+}
+
+
+/**
+ * \brief QP encode parts of an email TO/CC/BCC vector, and strip/filter invalid parts
+ * \param Recp Source list of email recipients
+ * \param UserName Temporary buffer for internal use; Please provide valid buffer.
+ * \param EmailAddress Temporary buffer for internal use; Please provide valid buffer.
+ * \param EncBuf Temporary buffer for internal use; Please provide valid buffer.
+ * \returns encoded & sanitized buffer with the contents of Recp; Caller owns this memory.
+ */
+StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp, 
+                                          StrBuf *UserName, 
+                                          StrBuf *EmailAddress,
+                                          StrBuf *EncBuf)
+{
+       StrBuf *Target;
+       int need_to_encode;
+
+       const char *pch, *pche;
+       const char *UserStart, *UserEnd, *EmailStart, *EmailEnd, *At;
+
+       if ((Recp == NULL) || (StrLength(Recp) == 0))
+               return NULL;
+
+       need_to_encode = 0;
+       pch = ChrPtr(Recp);
+       pche = pch + StrLength(Recp);
+
+       if (!CheckEncode(pch, -1, pche))
+               return NewStrBufDup(Recp);
+
+       Target = NewStrBufPlain(NULL, StrLength(Recp));
+
+       while ((pch != NULL) && (pch < pche))
+       {
+               int ColonOk = 0;
+
+               while (isspace(*pch)) pch++;
+               UserStart = UserEnd = EmailStart = EmailEnd = NULL;
+               
+               if ((*pch == '"') || (*pch == '\'')) {
+                       UserStart = pch + 1;
+                       
+                       UserEnd = strchr(UserStart, *pch);
+                       if (UserEnd == NULL) 
+                               break; ///TODO: Userfeedback??
+                       EmailStart = UserEnd + 1;
+                       while (isspace(*EmailStart))
+                               EmailStart++;
+                       if (UserEnd == UserStart) {
+                               UserStart = UserEnd = NULL;
+                       }
+                       
+                       if (*EmailStart == '<') {
+                               EmailStart++;
+                               EmailEnd = strchr(EmailStart, '>');
+                               if (EmailEnd == NULL)
+                                       EmailEnd = strchr(EmailStart, ',');
+                               
+                       }
+                       else {
+                               EmailEnd = strchr(EmailStart, ',');
+                       }
+                       if (EmailEnd == NULL)
+                               EmailEnd = pche;
+                       pch = EmailEnd + 1;
+                       ColonOk = 1;
+               }
+               else {
+                       int gt = 0;
+                       UserStart = pch;
+                       EmailEnd = strchr(UserStart, ',');
+                       if (EmailEnd == NULL) {
+                               EmailEnd = strchr(pch, '>');
+                               pch = NULL;
+                               if (EmailEnd != NULL) {
+                                       gt = 1;
+                               }
+                               else {
+                                       EmailEnd = pche;
+                               }
+                       }
+                       else {
+
+                               pch = EmailEnd + 1;
+                               while ((EmailEnd > UserStart) && !gt &&
+                                      ((*EmailEnd == ',') ||
+                                       (*EmailEnd == '>') ||
+                                       (isspace(*EmailEnd))))
+                               {
+                                       if (*EmailEnd == '>')
+                                               gt = 1;
+                                       else 
+                                               EmailEnd--;
+                               }
+                               if (EmailEnd == UserStart)
+                                       break;
+                       }
+                       if (gt) {
+                               EmailStart = strchr(UserStart, '<');
+                               if ((EmailStart == NULL) || (EmailStart > EmailEnd))
+                                       break;
+                               UserEnd = EmailStart;
+
+                               while ((UserEnd > UserStart) && 
+                                      isspace (*(UserEnd - 1)))
+                                       UserEnd --;
+                               EmailStart ++;
+                               if (UserStart >= UserEnd)
+                                       UserStart = UserEnd = NULL;
+                               At = strchr(EmailStart, '@');
+                       }
+                       else { /* this is a local recipient... no domain, just a realname */
+                               EmailStart = UserStart;
+                               At = strchr(EmailStart, '@');
+                               if (At == NULL) {
+                                       UserEnd = EmailEnd;
+                                       EmailEnd = NULL;
+                               }
+                               else {
+                                       EmailStart = UserStart;
+                                       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) && (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 ++;
+               if (pch != NULL) while (isspace(*pch))
+                       pch ++;
+       }
+       return Target;
+}
+
+
 /**
  * @ingroup StrBuf
  * @brief replaces all occurances of 'search' by 'replace'
@@ -2547,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));
-
-}
 
 
 /**
@@ -2669,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);
@@ -2684,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,
@@ -2709,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
@@ -2718,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
@@ -2746,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")) )
@@ -2771,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;
@@ -2855,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);
 }