BASE64: forcefully terminate it after deciphering it.
[citadel.git] / libcitadel / lib / stringbuf.c
index 86f83d805cd8869072214f6725765bb2b0d5d12b..436864690bd62d0af5eb9f638053de41ac9fe495 100644 (file)
@@ -32,6 +32,9 @@
 
 #include "libcitadel.h"
 
+#include "b64/cencode.h"
+#include "b64/cdecode.h"
+
 #ifdef HAVE_ICONV
 #include <iconv.h>
 #endif
@@ -1925,6 +1928,129 @@ void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
        *pt = '\0';
 }
 
+/** 
+ * @ingroup StrBuf_DeEnCoder
+ * @brief append a string with characters having a special meaning in xml encoded to the buffer
+ * @param OutBuf the output buffer
+ * @param In Buffer to encode
+ * @param PlainIn way in from plain old c strings
+ * @param PlainInLen way in from plain old c strings; maybe you've got binary data or know the length?
+ * @param OverrideLowChars should chars < 0x20 be replaced by _ or escaped as xml entity?
+ */
+void StrBufXMLEscAppend(StrBuf *OutBuf,
+                       const StrBuf *In,
+                       const char *PlainIn,
+                       long PlainInLen,
+                       int OverrideLowChars)
+{
+       const char *pch, *pche;
+       char *pt, *pte;
+       int IsUtf8Sequence;
+       int len;
+
+       if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
+               return;
+       if (PlainIn != NULL) {
+               if (PlainInLen < 0)
+                       len = strlen((const char*)PlainIn);
+               else
+                       len = PlainInLen;
+               pch = PlainIn;
+               pche = pch + len;
+       }
+       else {
+               pch = (const char*)In->buf;
+               pche = pch + In->BufUsed;
+               len = In->BufUsed;
+       }
+
+       if (len == 0)
+               return;
+
+       pt = OutBuf->buf + OutBuf->BufUsed;
+       /**< we max append 6 chars at once plus the \0 */
+       pte = OutBuf->buf + OutBuf->BufSize - 6;
+
+       while (pch < pche) {
+               if (pt >= pte) {
+                       OutBuf->BufUsed = pt - OutBuf->buf;
+                       IncreaseBuf(OutBuf, 1, -1);
+                       pte = OutBuf->buf + OutBuf->BufSize - 6;
+                       /**< we max append 3 chars at once plus the \0 */
+
+                       pt = OutBuf->buf + OutBuf->BufUsed;
+               }
+
+               if (*pch == '<') {
+                       memcpy(pt, HKEY("&lt;"));
+                       pt += 4;
+                       pch ++;
+               }
+               else if (*pch == '>') {
+                       memcpy(pt, HKEY("&gt;"));
+                       pt += 4;
+                       pch ++;
+               }
+               else if (*pch == '&') {
+                       memcpy(pt, HKEY("&amp;"));
+                       pt += 5;
+                       pch++;
+               }
+               else if ((*pch >= 0x20) && (*pch <= 0x7F)) {
+                       *pt = *pch;
+                       pt++; pch++;
+               }
+               else if (*pch < 0x20) {
+                       /* we probably shouldn't be doing this */
+                       if (OverrideLowChars)
+                       {
+                               *pt = '_';
+                               pt ++;
+                               pch ++;
+                       }
+                       else
+                       {
+                               *pt = '&';
+                               pt++;
+                               *pt = HexList[*(unsigned char*)pch][0];
+                               pt ++;
+                               *pt = HexList[*(unsigned char*)pch][1];
+                               pt ++; pch ++;
+                               *pt = '&';
+                               pt++;
+                               pch ++;
+                       }
+               }
+               else {
+                       IsUtf8Sequence =  Ctdl_GetUtf8SequenceLength(pch, pche);
+                       if (IsUtf8Sequence)
+                       {
+                               while (IsUtf8Sequence > 0){
+                                       *pt = *pch;
+                                       pt ++;
+                                       pch ++;
+                                       --IsUtf8Sequence;
+                               }
+                       }
+                       else
+                       {
+                               *pt = '&';
+                               pt++;
+                               *pt = HexList[*(unsigned char*)pch][0];
+                               pt ++;
+                               *pt = HexList[*(unsigned char*)pch][1];
+                               pt ++; pch ++;
+                               *pt = '&';
+                               pt++;
+                               pch ++;
+                       }
+               }
+       }
+       *pt = '\0';
+       OutBuf->BufUsed = pt - OutBuf->buf;
+}
+
+
 /** 
  * @ingroup StrBuf_DeEnCoder
  * @brief append a string in hex encoding to the buffer
@@ -2697,9 +2823,81 @@ int StrBufDecodeBase64(StrBuf *Buf)
        free(Buf->buf);
        Buf->buf = xferbuf;
        Buf->BufUsed = siz;
+
+       Buf->buf[Buf->BufUsed] = '\0';
        return siz;
 }
 
+/**
+ * @ingroup StrBuf_DeEnCoder
+ * @brief decode a buffer from base 64 encoding; expects targetbuffer
+ * @param BufIn Buffor to transform
+ * @param BufOut Buffer to put result into
+ */
+int StrBufDecodeBase64To(const StrBuf *BufIn, StrBuf *BufOut)
+{
+       if ((BufIn == NULL) || (BufOut == NULL))
+               return -1;
+
+       if (BufOut->BufSize < BufIn->BufUsed)
+               IncreaseBuf(BufOut, BufIn->BufUsed, 0);
+
+       BufOut->BufUsed = CtdlDecodeBase64(BufOut->buf,
+                                          BufIn->buf,
+                                          BufIn->BufUsed);
+       return BufOut->BufUsed;
+}
+
+void *StrBufNewStreamContext(eStreamType type)
+{
+       base64_decodestate *state;;
+
+       switch (type)
+       {
+       case eBase64Decode:
+               state = (base64_decodestate*) malloc(sizeof(base64_decodestate));
+               base64_init_decodestate(state);
+               return state;
+               break;
+       }
+       return NULL;
+}
+
+void StrBufDestroyStreamContext(eStreamType type, void **Stream)
+{
+       switch (type)
+       {
+       case eBase64Decode:
+               free(*Stream);
+               *Stream = NULL;
+               break;
+       }
+}
+void StrBufStreamDecodeTo(StrBuf *Target, const StrBuf *In, const char* pIn, long pInLen, void *Stream)
+{
+       base64_decodestate *state = Stream;
+       long ExpectLen;
+
+       if (In != NULL)
+       {
+               pIn = In->buf;
+               pInLen = In->BufUsed;
+       }
+       if ((pIn == NULL) || (Stream == NULL))
+               return;
+       
+       ExpectLen = (pInLen / 4) * 3;
+
+       if (Target->BufSize - Target->BufUsed < ExpectLen)
+       {
+               IncreaseBuf(Target, 1, Target->BufUsed + ExpectLen + 1);
+       }
+
+       ExpectLen = base64_decode_block(pIn, pInLen, Target->buf + Target->BufUsed, state);
+       Target->BufUsed += ExpectLen;
+       Target->buf[Target->BufUsed] = '\0';
+}
+
 /**
  * @ingroup StrBuf_DeEnCoder
  * @brief decode a buffer from base 64 encoding; destroys original
@@ -2854,7 +3052,6 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
                ch = (unsigned char) source->buf[i];
                if ((ch  <  32) || 
                    (ch  > 126) || 
-                   (ch ==  61) ||
                    (ch == '=') ||
                     (ch == '?') ||
                    (ch == '_') ||
@@ -2909,24 +3106,22 @@ StrBuf *StrBufRFC2047encodeMessage(const StrBuf *EncodeMe)
                {
                        long Offset;
                        Offset = Optr - OutBuf->buf;
+                       OutBuf->BufUsed = Optr - OutBuf->buf;
                        IncreaseBuf(OutBuf, 1, 0);
                        Optr = OutBuf->buf + Offset;
                        OEptr = OutBuf->buf + OutBuf->BufSize;
                }
-               if ((*ptr == '\r') || (*ptr == '\n'))
+               if (*ptr == '\r')
                {
                        /* ignore carriage returns */
                        ptr ++;
                }
-               else if (*ptr == 10) {
+               else if (*ptr == '\n') {
                        /* hard line break */
-                       if ((LinePos > 0) && (isspace(*(Optr-1))))
-                       {
-                               memcpy(Optr, HKEY("=0A"));
-                               Optr += 3;
-                       }
+                       memcpy(Optr, HKEY("=0A"));
+                       Optr += 3;
+                       LinePos += 3;
                        ptr ++;
-                       LinePos = 0;
                }
                else if (( (*ptr >= 32) && (*ptr <= 60) ) ||
                         ( (*ptr >= 62) && (*ptr <= 126) ))
@@ -4066,15 +4261,23 @@ inline static void FDIOBufferFlush(FDIOBuffer *FDB)
 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
 {
        FDIOBufferFlush(FDB);
-       FDB->ChunkSize = 
-               FDB->TotalSendSize = TotalSendSize;
+
+       FDB->TotalSendSize = TotalSendSize;
+       if (TotalSendSize > 0)
+               FDB->ChunkSize = TotalSendSize;
+       else
+       {
+               TotalSendSize = SIZ * 10;
+               FDB->ChunkSize = TotalSendSize;
+       }
        FDB->IOB = IO;
+
 #ifdef LINUX_SPLICE
        if (EnableSplice)
                pipe(FDB->SplicePipe);
        else
 #endif
-               FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
+               FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize+ 1);
 
        FDB->OtherFD = FD;
 }
@@ -4101,68 +4304,159 @@ void FDIOBufferDelete(FDIOBuffer *FDB)
 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
 {
        ssize_t sent, pipesize;
-#ifdef LINUX_SPLICE
-       if (EnableSplice)
+
+       if (FDB->TotalSendSize > 0)
        {
-               if (FDB->PipeSize == 0)
+#ifdef LINUX_SPLICE
+               if (EnableSplice)
                {
-                       pipesize = splice(FDB->OtherFD,
-                                         &FDB->TotalSentAlready, 
-                                         FDB->SplicePipe[1],
-                                         NULL, 
-                                         FDB->ChunkSendRemain, 
-                                         SPLICE_F_MOVE);
+                       if (FDB->PipeSize == 0)
+                       {
+                               pipesize = splice(FDB->OtherFD,
+                                                 &FDB->TotalSentAlready, 
+                                                 FDB->SplicePipe[1],
+                                                 NULL, 
+                                                 FDB->ChunkSendRemain, 
+                                                 SPLICE_F_MOVE);
        
-                       if (pipesize == -1)
+                               if (pipesize == -1)
+                               {
+                                       *Err = strerror(errno);
+                                       return pipesize;
+                               }
+                               FDB->PipeSize = pipesize;
+                       }
+                       sent =  splice(FDB->SplicePipe[0],
+                                      NULL, 
+                                      FDB->IOB->fd,
+                                      NULL, 
+                                      FDB->PipeSize,
+                                      SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+                       if (sent == -1)
                        {
                                *Err = strerror(errno);
-                               return pipesize;
+                               return sent;
                        }
-                       FDB->PipeSize = pipesize;
+                       FDB->PipeSize -= sent;
+                       FDB->ChunkSendRemain -= sent;
+                       return sent;
                }
-               sent =  splice(FDB->SplicePipe[0],
-                              NULL, 
-                              FDB->IOB->fd,
-                              NULL, 
-                              FDB->PipeSize,
-                              SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
-               if (sent == -1)
+               else
+#endif
                {
-                       *Err = strerror(errno);
-                       return sent;
+                       char *pRead;
+                       long nRead = 0;
+
+                       pRead = FDB->ChunkBuffer->buf;
+                       while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
+                       {
+                               nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
+                               if (nRead > 0) {
+                                       FDB->ChunkBuffer->BufUsed += nRead;
+                                       FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
+                               }
+                               else if (nRead == 0) {}
+                               else return nRead;
+                       }
+
+                       nRead = write(FDB->IOB->fd,
+                                     FDB->ChunkBuffer->buf     + FDB->TotalSentAlready,
+                                     FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
+
+                       if (nRead >= 0) {
+                               FDB->TotalSentAlready += nRead;
+                               FDB->ChunkSendRemain -= nRead;
+                               return FDB->ChunkSendRemain;
+                       }
+                       else {
+                               return nRead;
+                       }
                }
-               FDB->PipeSize -= sent;
-               FDB->ChunkSendRemain -= sent;
-               return sent;
        }
        else
-#endif
        {
-               char *pRead;
-               long nRead = 0;
-
-               pRead = FDB->ChunkBuffer->buf;
-               while ((FDB->ChunkBuffer->BufUsed < FDB->TotalSendSize) && (nRead >= 0))
+#ifdef LINUX_SPLICE
+               if (EnableSplice)
                {
-                       nRead = read(FDB->OtherFD, pRead, FDB->TotalSendSize - FDB->ChunkBuffer->BufUsed);
-                       if (nRead > 0) {
-                               FDB->ChunkBuffer->BufUsed += nRead;
-                               FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
+                       if (FDB->PipeSize == 0)
+                       {
+                               pipesize = splice(FDB->OtherFD,
+                                                 &FDB->TotalSentAlready, 
+                                                 FDB->SplicePipe[1],
+                                                 NULL, 
+                                                 SIZ * 10, 
+                                                 SPLICE_F_MOVE);
+       
+                               if (pipesize == -1)
+                               {
+                                       *Err = strerror(errno);
+                                       return pipesize;
+                               }
+                               FDB->PipeSize = pipesize;
+                               if (pipesize == 0)
+                                       return -1;
                        }
-                       else if (nRead == 0) {}
-                       else return nRead;
-               
+                       sent =  splice(FDB->SplicePipe[0],
+                                      NULL, 
+                                      FDB->IOB->fd,
+                                      NULL, 
+                                      FDB->PipeSize,
+                                      SPLICE_F_MORE | SPLICE_F_MOVE | SPLICE_F_NONBLOCK);
+                       if (sent == -1)
+                       {
+                               *Err = strerror(errno);
+                               return sent;
+                       }
+                       FDB->PipeSize -= sent;
+                       FDB->ChunkSendRemain -= sent;
+                       return sent;
                }
+               else
+#endif
+               {
+                       char *pRead;
+                       long nRead = 0;
+
+                       pRead = FDB->ChunkBuffer->buf;
+                       while ((FDB->ChunkSendRemain == 0) && 
+                              (FDB->ChunkBuffer->BufUsed < FDB->ChunkBuffer->BufSize) &&
+                              (nRead >= 0))
+                       {
+                               FDB->TotalSentAlready = 0;
+                               nRead = read(FDB->OtherFD, pRead, FDB->ChunkBuffer->BufSize - FDB->ChunkBuffer->BufUsed);
+                               if (nRead > 0) {
+                                       FDB->ChunkBuffer->BufUsed += nRead;
+                                       FDB->ChunkBuffer->buf[FDB->ChunkBuffer->BufUsed] = '\0';
+                                       FDB->ChunkSendRemain += nRead;
+                               }
+                               else if (nRead == 0)
+                               {
+                                       return -1;
+                               }
+                               else
+                               {
+                                       *Err = strerror(errno);
+                                       return nRead;
+                               }
+                       }
 
-               nRead = write(FDB->IOB->fd, FDB->ChunkBuffer->buf + FDB->TotalSentAlready, FDB->ChunkSendRemain);
+                       nRead = write(FDB->IOB->fd,
+                                     FDB->ChunkBuffer->buf     + FDB->TotalSentAlready,
+                                     FDB->ChunkBuffer->BufUsed - FDB->TotalSentAlready);
 
-               if (nRead >= 0) {
-                       FDB->TotalSentAlready += nRead;
-                       FDB->ChunkSendRemain -= nRead;
-                       return FDB->ChunkSendRemain;
-               }
-               else {
-                       return nRead;
+                       if (nRead >= 0) {
+                               FDB->TotalSentAlready += nRead;
+                               FDB->ChunkSendRemain -= nRead;
+                               if (FDB->ChunkSendRemain == 0)
+                               {
+                                       FDB->ChunkBuffer->BufUsed = 0;
+                                       FDB->TotalSentAlready = 0;
+                               }
+                               return FDB->ChunkSendRemain;
+                       }
+                       else {
+                               return nRead;
+                       }
                }
        }
 }