]> code.citadel.org Git - citadel.git/blobdiff - libcitadel/lib/stringbuf.c
StrBuf_NextToken: if the source is empty, flush the target.
[citadel.git] / libcitadel / lib / stringbuf.c
index d6d960dabddcf3fc8db9cd960b549e5ace174872..dfba39d7e13e904ffc202186139a53a90bdfc40f 100644 (file)
@@ -1502,6 +1502,8 @@ int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pSt
            (Source->BufUsed == 0)      ) 
        {
                *pStart = StrBufNOTNULL;
+               if (dest != NULL)
+                       FlushStrBuf(dest);
                return -1;
        }
         
@@ -2649,7 +2651,6 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
                                pch = NULL;
                                if (EmailEnd != NULL) {
                                        gt = 1;
-                                       EmailEnd --;
                                }
                                else {
                                        EmailEnd = pche;
@@ -2675,7 +2676,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;
@@ -2942,6 +2947,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);
@@ -2957,6 +2964,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,
@@ -2982,7 +2992,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
@@ -2991,9 +3001,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
@@ -3019,7 +3062,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")) )
@@ -3044,12 +3086,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;
@@ -3128,8 +3168,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);
 }
 
@@ -3356,7 +3394,191 @@ int CompressBuffer(StrBuf *Buf)
        return 0;
 }
 
+/*******************************************************************************
+ *           File I/O; Callbacks to libevent                                   *
+ *******************************************************************************/
+
+long StrBuf_read_one_chunk_callback (int fd, short event, IOBuffer *FB)
+{
+       long bufremain = 0;
+       int n;
 
+       /*
+        * check whether the read pointer is somewhere in a range 
+        * where a cut left is inexpensive
+        */
+       if (FB->ReadWritePointer != NULL)
+       {
+               long already_read = FB->ReadWritePointer - FB->Buf->buf;
+               bufremain = FB->Buf->BufSize - FB->Buf->BufUsed;
+
+               if (already_read != 0) {
+                       long unread = FB->Buf->BufUsed - already_read;
+                       /* else nothing to compact... */
+                       if (unread == 0) {
+                               FB->ReadWritePointer = FB->Buf->buf;
+                               bufremain = FB->Buf->BufSize;                   
+                       }
+                       else if ((unread < 64) || 
+                                (bufremain < already_read))
+                       {
+                               /* 
+                                * if its just a tiny bit remaining, or we run out of space... 
+                                * lets tidy up.
+                                */
+                               FB->Buf->BufUsed = unread;
+                               if (unread < already_read)
+                                       memcpy(FB->Buf->buf, FB->ReadWritePointer, unread);
+                               else
+                                       memmove(FB->Buf->buf, FB->ReadWritePointer, unread);
+                               FB->ReadWritePointer = FB->Buf->buf;
+                               bufremain = FB->Buf->BufSize - unread;
+                       }
+                       else if (bufremain < (FB->Buf->BufSize / 10)) {
+                               /* get a bigger buffer */ ///TODO: special increase function that won't copy the already read!
+                               IncreaseBuf(FB->Buf, 0, -1);
+                               FB->ReadWritePointer = FB->Buf->buf + unread;
+                               bufremain = FB->Buf->BufSize - unread;
+                       }
+               }
+               
+       }
+       else {
+               FB->ReadWritePointer = FB->Buf->buf;
+               bufremain = FB->Buf->BufSize;
+       }
+       n = read(fd, FB->Buf->buf + FB->Buf->BufUsed, bufremain - 1);
+
+       if (n > 0) {
+               FB->Buf->BufUsed += n;
+               FB->Buf->buf[FB->Buf->BufUsed] = '\0';
+       }
+       return n;
+}
+
+int StrBuf_write_one_chunk_callback(int fd, short event, IOBuffer *FB)
+{
+       long WriteRemain;
+       int n;
+
+       if (FB->ReadWritePointer != NULL)
+       {
+               WriteRemain = FB->Buf->BufUsed - 
+                       (FB->ReadWritePointer - 
+                        FB->Buf->buf);
+       }
+       else {
+               FB->ReadWritePointer = FB->Buf->buf;
+               WriteRemain = FB->Buf->BufUsed;
+       }
+
+       n = write(fd, FB->ReadWritePointer, WriteRemain);
+       if (n > 0) {
+               FB->ReadWritePointer += n;
+
+               if (FB->ReadWritePointer == 
+                   FB->Buf->buf + FB->Buf->BufUsed)
+               {
+                       FlushStrBuf(FB->Buf);
+                       FB->ReadWritePointer = NULL;
+                       return 0;
+               }
+       // check whether we've got something to write
+       // get the maximum chunk plus the pointer we can send
+       // write whats there
+       // if not all was sent, remember the send pointer for the next time
+               return FB->ReadWritePointer - FB->Buf->buf + FB->Buf->BufUsed;
+       }
+       return n;
+}
+
+/**
+ * @ingroup StrBuf_IO
+ * @brief extract a "next line" from Buf; Ptr to persist across several iterations
+ * @param LineBuf your line will be copied here.
+ * @param FB BLOB with lines of text...
+ * @param Ptr moved arround to keep the next-line across several iterations
+ *        has to be &NULL on start; will be &NotNULL on end of buffer
+ * @returns size of copied buffer
+ */
+eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
+{
+       const char *aptr, *ptr, *eptr;
+       char *optr, *xptr;
+
+       if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
+               FB->ReadWritePointer = StrBufNOTNULL;
+               return eReadFail;
+       }
+
+       FlushStrBuf(LineBuf);
+       if (FB->ReadWritePointer == NULL)
+               ptr = aptr = FB->Buf->buf;
+       else
+               ptr = aptr = FB->ReadWritePointer;
+
+       optr = LineBuf->buf;
+       eptr = FB->Buf->buf + FB->Buf->BufUsed;
+       xptr = LineBuf->buf + LineBuf->BufSize - 1;
+
+       while ((ptr <= eptr) && 
+              (*ptr != '\n') &&
+              (*ptr != '\r') )
+       {
+               *optr = *ptr;
+               optr++; ptr++;
+               if (optr == xptr) {
+                       LineBuf->BufUsed = optr - LineBuf->buf;
+                       IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
+                       optr = LineBuf->buf + LineBuf->BufUsed;
+                       xptr = LineBuf->buf + LineBuf->BufSize - 1;
+               }
+       }
+
+       if (ptr >= eptr) {
+               if (optr > LineBuf->buf)
+                       optr --;
+               if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) {
+                       LineBuf->BufUsed = optr - LineBuf->buf;
+                       *optr = '\0';       
+                       return eMustReadMore;
+               }
+       }
+       LineBuf->BufUsed = optr - LineBuf->buf;
+       *optr = '\0';       
+       if ((ptr <= eptr) && (*ptr == '\r'))
+               ptr ++;
+       if ((ptr <= eptr) && (*ptr == '\n'))
+               ptr ++;
+       
+       if (ptr < eptr) {
+               FB->ReadWritePointer = ptr;
+       }
+       else {
+               FlushStrBuf(FB->Buf);
+               FB->ReadWritePointer = NULL;
+       }
+
+       return eReadSuccess;
+}
+
+/**
+ * @ingroup StrBuf_CHUNKED_IO
+ * @brief check whether the chunk-buffer has more data waiting or not.
+ * @param FB Chunk-Buffer to inspect
+ */
+eReadState StrBufCheckBuffer(IOBuffer *FB)
+{
+       if (FB == NULL)
+               return eReadFail;
+       if (FB->Buf->BufUsed == 0)
+               return eReadSuccess;
+       if (FB->ReadWritePointer == NULL)
+               return eBufferNotEmpty;
+       if (FB->Buf->buf + FB->Buf->BufUsed > FB->ReadWritePointer)
+               return eBufferNotEmpty;
+       return eReadSuccess;
+}
 
 /*******************************************************************************
  *           File I/O; Prefer buffered read since its faster!                  *