FDIO: make splice runtime optional
[citadel.git] / libcitadel / lib / stringbuf.c
index 795dd604d734f881f0792d6398e44a3886cd3871..963c55f6a7fbe1a398751e6f4893bf31d7f9df81 100644 (file)
@@ -29,9 +29,7 @@
 #include <sys/types.h>
 #define SHOW_ME_VAPPEND_PRINTF
 #include <stdarg.h>
-#ifndef LINUX_SENDFILE
-#include <sys/sendfile.h>
-#endif
+
 #include "libcitadel.h"
 
 #ifdef HAVE_ICONV
@@ -52,6 +50,7 @@ int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
                           const Bytef * source, uLong sourceLen, int level);
 #endif
 int BaseStrBufSize = 64;
+int EnableSplice = 0;
 
 const char *StrBufNOTNULL = ((char*) NULL) - 1;
 
@@ -513,6 +512,7 @@ StrBuf* NewStrBufPlain(const char* ptr, int nChars)
 
        if (Siz == 0)
        {
+               free(NewBuf);
                return NULL;
        }
 
@@ -613,7 +613,7 @@ StrBuf* _NewConstStrBuf(const char* StringConstant, size_t SizeOfStrConstant)
  */
 int FlushStrBuf(StrBuf *buf)
 {
-       if (buf == NULL)
+       if ((buf == NULL) || (buf->buf == NULL))
                return -1;
        if (buf->ConstBuf)
                return -1;       
@@ -817,7 +817,8 @@ long StrBufPook(StrBuf *Buf, const char* ptr, long nThChar, long nChars, char Po
  */
 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
 {
-       if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
+       if ((AppendBuf == NULL) || (AppendBuf->buf == NULL) ||
+           (Buf == NULL) || (Buf->buf == NULL))
                return;
 
        if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
@@ -1064,7 +1065,9 @@ void StrBufCutLeft(StrBuf *Buf, int nChars)
  */
 void StrBufCutRight(StrBuf *Buf, int nChars)
 {
-       if ((Buf == NULL) || (Buf->BufUsed == 0)) return;
+       if ((Buf == NULL) || (Buf->BufUsed == 0) || (Buf->buf == NULL))
+               return;
+
        if (nChars >= Buf->BufUsed) {
                FlushStrBuf(Buf);
                return;
@@ -1145,7 +1148,7 @@ void StrBufStripAllBut(StrBuf *Buf, char leftboundary, char rightboundary)
        const char *pLeft;
        const char *pRight;
 
-       if (Buf == NULL)
+       if ((Buf == NULL) || (Buf->buf == NULL))
                return;
        pLeft = pBuff = Buf->buf;
        while (pBuff != NULL) {
@@ -1343,6 +1346,23 @@ int StrBufRemove_token(StrBuf *Source, int parmnum, char separator)
        return ReducedBy;
 }
 
+int StrBufExtract_tokenFromStr(StrBuf *dest, const char *Source, long SourceLen, int parmnum, char separator)
+{
+       const StrBuf Temp = {
+               (char*)Source,
+               SourceLen,
+               SourceLen,
+               1
+#ifdef SIZE_DEBUG
+               ,
+               0,
+               "",
+               ""
+#endif
+       };
+
+       return StrBufExtract_token(dest, &Temp, parmnum, separator);
+}
 
 /**
  * @ingroup StrBuf_Tokenizer
@@ -1823,6 +1843,66 @@ void StrBufUrlescAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
        *pt = '\0';
 }
 
+/** 
+ * @ingroup StrBuf_DeEnCoder
+ * @brief Escape a string for feeding out as a the username/password part of an URL while appending it to a Buffer
+ * @param OutBuf the output buffer
+ * @param In Buffer to encode
+ * @param PlainIn way in from plain old c strings
+ */
+void StrBufUrlescUPAppend(StrBuf *OutBuf, const StrBuf *In, const char *PlainIn)
+{
+       const char *pch, *pche;
+       char *pt, *pte;
+       int len;
+       
+       if (((In == NULL) && (PlainIn == NULL)) || (OutBuf == NULL) )
+               return;
+       if (PlainIn != NULL) {
+               len = strlen(PlainIn);
+               pch = PlainIn;
+               pche = pch + len;
+       }
+       else {
+               pch = In->buf;
+               pche = pch + In->BufUsed;
+               len = In->BufUsed;
+       }
+
+       if (len == 0) 
+               return;
+
+       pt = OutBuf->buf + OutBuf->BufUsed;
+       pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
+
+       while (pch < pche) {
+               if (pt >= pte) {
+                       IncreaseBuf(OutBuf, 1, -1);
+                       pte = OutBuf->buf + OutBuf->BufSize - 4; /**< we max append 3 chars at once plus the \0 */
+                       pt = OutBuf->buf + OutBuf->BufUsed;
+               }
+
+               if((*pch >= 'a' && *pch <= 'z') ||
+                  (*pch >= 'A' && *pch <= 'Z') || /* A-Z */
+                  (*pch >= '0' && *pch <= ':') || /* 0-9 : */
+                  (*pch == '!') || (*pch == '_') || 
+                  (*pch == ',') || (*pch == '.'))
+               {
+                       *(pt++) = *(pch++);
+                       OutBuf->BufUsed++;
+               }                       
+               else {
+                       *pt = '%';
+                       *(pt + 1) = HexList[(unsigned char)*pch][0];
+                       *(pt + 2) = HexList[(unsigned char)*pch][1];
+                       pt += 3;
+                       OutBuf->BufUsed += 3;
+                       pch ++;
+               }
+       }
+       *pt = '\0';
+}
+
 /** 
  * @ingroup StrBuf_DeEnCoder
  * @brief append a string in hex encoding to the buffer
@@ -2442,14 +2522,14 @@ void StrBufEUid_unescapize(StrBuf *target, const StrBuf *source)
        int a, b, len;
        char hex[3];
 
-       if (target != NULL)
-               FlushStrBuf(target);
-
-       if (source == NULL ||target == NULL)
+       if ((source == NULL) || (target == NULL) || (target->buf == NULL))
        {
                return;
        }
 
+       if (target != NULL)
+               FlushStrBuf(target);
+
        len = source->BufUsed;
        for (a = 0; a < len; ++a) {
                if (target->BufUsed >= target->BufSize)
@@ -2486,7 +2566,7 @@ void StrBufEUid_escapize(StrBuf *target, const StrBuf *source)
        if (target != NULL)
                FlushStrBuf(target);
 
-       if (source == NULL ||target == NULL)
+       if ((source == NULL) || (target == NULL) || (target->buf == NULL))
        {
                return;
        }
@@ -2674,7 +2754,10 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
                        FlushStrBuf(*target);
                        StrBufAppendBuf(*target, source, 0);
                }
-               return (*target)->BufUsed;
+               if (*target != 0)
+                       return (*target)->BufUsed;
+               else
+                       return 0;
        }
        if (*target == NULL)
                *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
@@ -2773,7 +2856,7 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
        while ((pch != NULL) && (pch < pche))
        {
                while (isspace(*pch)) pch++;
-               UserStart = UserEnd = EmailStart = EmailEnd = NULL;
+               UserEnd = EmailStart = EmailEnd = NULL;
                
                if ((*pch == '"') || (*pch == '\'')) {
                        UserStart = pch + 1;
@@ -2844,7 +2927,6 @@ StrBuf *StrBufSanitizeEmailRecipientVector(const StrBuf *Recp,
                                EmailStart ++;
                                if (UserStart >= UserEnd)
                                        UserStart = UserEnd = NULL;
-                               At = strchr(EmailStart, '@');
                        }
                        else { /* this is a local recipient... no domain, just a realname */
                                EmailStart = UserStart;
@@ -3023,6 +3105,9 @@ void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
        size_t obuflen;                 /**< Length of output buffer */
 
 
+       if ((ConvertBuf == NULL) || (TmpBuf == NULL))
+               return;
+
        /* since we're converting to utf-8, one glyph may take up to 6 bytes */
        if (ConvertBuf->BufUsed * 6 >= TmpBuf->BufSize)
                IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed * 6);
@@ -3205,9 +3290,12 @@ void StrBuf_RFC822_2_Utf8(StrBuf *Target,
 #endif
        const char *eptr;
        int passes = 0;
-       int i, len;
+       int i;
        int illegal_non_rfc2047_encoding = 0;
 
+
+       if (DecodeMe == NULL)
+               return;
        /* Sometimes, badly formed messages contain strings which were simply
         *  written out directly in some foreign character set instead of
         *  using RFC2047 encoding.  This is illegal but we will attempt to
@@ -3215,7 +3303,6 @@ void StrBuf_RFC822_2_Utf8(StrBuf *Target,
         *  charset to UTF-8 if we see any nonprintable characters.
         */
        
-       len = StrLength(DecodeMe);
        for (i=0; i<DecodeMe->BufUsed; ++i) {
                if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) {
                        illegal_non_rfc2047_encoding = 1;
@@ -3239,8 +3326,7 @@ void StrBuf_RFC822_2_Utf8(StrBuf *Target,
        }
 
        /* pre evaluate the first pair */
-       nextend = end = NULL;
-       len = StrLength(DecodeMee);
+       end = NULL;
        start = strstr(DecodeMee->buf, "=?");
        eptr = DecodeMee->buf + DecodeMee->BufUsed;
        if (start != NULL) 
@@ -3257,7 +3343,6 @@ void StrBuf_RFC822_2_Utf8(StrBuf *Target,
                
                nFront = start - DecodeMee->buf;
                StrBufAppendBufPlain(Target, DecodeMee->buf, nFront, 0);
-               len -= nFront;
        }
        /*
         * Since spammers will go to all sorts of absurd lengths to get their
@@ -3697,6 +3782,10 @@ eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
        const char *aptr, *ptr, *eptr;
        char *optr, *xptr;
 
+       if ((FB == NULL) || (LineBuf == NULL) || (LineBuf->buf == NULL))
+               return eReadFail;
+       
+
        if ((FB->Buf == NULL) || (FB->ReadWritePointer == StrBufNOTNULL)) {
                FB->ReadWritePointer = StrBufNOTNULL;
                return eReadFail;
@@ -3784,87 +3873,257 @@ eReadState StrBufCheckBuffer(IOBuffer *FB)
 
 long IOBufferStrLength(IOBuffer *FB)
 {
+       if ((FB == NULL) || (FB->Buf == NULL))
+               return 0;
        if (FB->ReadWritePointer == NULL)
                return StrLength(FB->Buf);
        
        return StrLength(FB->Buf) - (FB->ReadWritePointer - FB->Buf->buf);
 }
 
-
-
 void FDIOBufferInit(FDIOBuffer *FDB, IOBuffer *IO, int FD, long TotalSendSize)
 {
        memset(FDB, 0, sizeof(FDIOBuffer));
        FDB->ChunkSize = 
                FDB->TotalSendSize = TotalSendSize;
        FDB->IOB = IO;
-#ifndef LINUX_SENDFILE
-       FDB->ChunkBuffer = NewStrBuf();
-#else
-       pipe(FDB->SplicePipe);
+#ifdef LINUX_SPLICE
+       if (EnableSplice)
+               pipe(FDB->SplicePipe);
+       else
 #endif
+               FDB->ChunkBuffer = NewStrBufPlain(NULL, TotalSendSize + 1);
+
        FDB->OtherFD = FD;
 }
 
 void FDIOBufferDelete(FDIOBuffer *FDB)
 {
-#ifndef LINUX_SENDFILE
-       FreeStrBuf(&FDB->ChunkBuffer);
-#else
-       close(FDB->SplicePipe[0]);
-       close(FDB->SplicePipe[1]);
+#ifdef LINUX_SPLICE
+       if (EnableSplice)
+       {
+               close(FDB->SplicePipe[0]);
+               close(FDB->SplicePipe[1]);
+       }
+       else
 #endif
+               FreeStrBuf(&FDB->ChunkBuffer);
+       
        close(FDB->OtherFD);
        memset(FDB, 0, sizeof(FDIOBuffer));     
 }
 
 int FileSendChunked(FDIOBuffer *FDB, const char **Err)
 {
-
-#ifdef LINUX_SENDFILE
-       ssize_t sent;
-       sent = sendfile(FDB->IOB->fd, FDB->OtherFD, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
-       if (sent == -1)
+       ssize_t sent, pipesize;
+#ifdef LINUX_SPLICE
+       if (EnableSplice)
        {
-               *Err = strerror(errno);
+               if (FDB->PipeSize == 0)
+               {
+                       pipesize = splice(FDB->OtherFD,
+                                         &FDB->TotalSentAlready, 
+                                         FDB->SplicePipe[1],
+                                         NULL, 
+                                         FDB->ChunkSendRemain, 
+                                         SPLICE_F_MOVE);
+       
+                       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 sent;
+               }
+               FDB->PipeSize -= sent;
+               FDB->ChunkSendRemain -= sent;
                return sent;
        }
-       FDB->ChunkSendRemain -= sent;
-       return FDB->ChunkSendRemain;
-#else
+       else
 #endif
-       return 0;
+       {
+               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->ChunkSendRemain);
+
+               if (nRead >= 0) {
+                       FDB->TotalSentAlready += nRead;
+                       FDB->ChunkSendRemain -= nRead;
+                       return FDB->ChunkSendRemain;
+               }
+               else {
+                       return nRead;
+               }
+       }
 }
 
 int FileRecvChunked(FDIOBuffer *FDB, const char **Err)
 {
-
-#ifdef LINUX_SENDFILE
        ssize_t sent, pipesize;
 
-       pipesize = splice(FDB->IOB->fd, NULL, 
-                         FDB->SplicePipe[1], NULL, 
-                         FDB->ChunkSendRemain, 
-                         SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
-       if (pipesize == -1)
+#ifdef LINUX_SPLICE
+       if (EnableSplice)
        {
-               *Err = strerror(errno);
-               return pipesize;
-       }
+               if (FDB->PipeSize == 0)
+               {
+                       pipesize = splice(FDB->IOB->fd,
+                                         NULL, 
+                                         FDB->SplicePipe[1],
+                                         NULL, 
+                                         FDB->ChunkSendRemain, 
+                                         SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
+
+                       if (pipesize == -1)
+                       {
+                               *Err = strerror(errno);
+                               return pipesize;
+                       }
+                       FDB->PipeSize = pipesize;
+               }
        
-       sent = splice(FDB->SplicePipe[0], NULL, 
-                     FDB->OtherFD, &FDB->TotalSentAlready, 
-                     pipesize, SPLICE_F_MORE | SPLICE_F_MOVE);
-       if (sent == -1)
-       {
-               *Err = strerror(errno);
+               sent = splice(FDB->SplicePipe[0],
+                             NULL, 
+                             FDB->OtherFD,
+                             &FDB->TotalSentAlready, 
+                             FDB->PipeSize,
+                             SPLICE_F_MORE | SPLICE_F_MOVE);
+
+               if (sent == -1)
+               {
+                       *Err = strerror(errno);
+                       return sent;
+               }
+               FDB->PipeSize -= sent;
+               FDB->ChunkSendRemain -= sent;
                return sent;
        }
-       FDB->ChunkSendRemain -= sent;
-       return sent;
-#else
+       else
 #endif
-       return 0;
+       {
+               sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
+               if (sent > 0) {
+                       int nWritten = 0;
+                       int rc; 
+               
+                       FDB->ChunkBuffer->BufUsed = sent;
+
+                       while (nWritten < FDB->ChunkBuffer->BufUsed) {
+                               rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
+                               if (rc < 0) {
+                                       *Err = strerror(errno);
+                                       return rc;
+                               }
+                               nWritten += rc;
+
+                       }
+                       FDB->ChunkBuffer->BufUsed = 0;
+                       FDB->TotalSentAlready += sent;
+                       FDB->ChunkSendRemain -= sent;
+                       return FDB->ChunkSendRemain;
+               }
+               else if (sent < 0) {
+                       *Err = strerror(errno);
+                       return sent;
+               }
+               return 0;
+       }
+}
+
+int FileMoveChunked(FDIOBuffer *FDB, const char **Err)
+{
+       ssize_t sent, pipesize;
+
+#ifdef LINUX_SPLICE
+       if (EnableSplice)
+       {
+               if (FDB->PipeSize == 0)
+               {
+                       pipesize = splice(FDB->IOB->fd,
+                                         &FDB->TotalReadAlready, 
+                                         FDB->SplicePipe[1],
+                                         NULL, 
+                                         FDB->ChunkSendRemain, 
+                                         SPLICE_F_MORE | SPLICE_F_MOVE|SPLICE_F_NONBLOCK);
+                       
+                       if (pipesize == -1)
+                       {
+                               *Err = strerror(errno);
+                               return pipesize;
+                       }
+                       FDB->PipeSize = pipesize;
+               }
+               
+               sent = splice(FDB->SplicePipe[0],
+                             NULL, 
+                             FDB->OtherFD,
+                             &FDB->TotalSentAlready, 
+                             FDB->PipeSize,
+                             SPLICE_F_MORE | SPLICE_F_MOVE);
+               
+               if (sent == -1)
+               {
+                       *Err = strerror(errno);
+                       return sent;
+               }
+               FDB->PipeSize -= sent;
+               FDB->ChunkSendRemain -= sent;
+               return sent;
+       }
+       else
+#endif 
+       {
+               sent = read(FDB->IOB->fd, FDB->ChunkBuffer->buf, FDB->ChunkSendRemain);
+               if (sent > 0) {
+                       int nWritten = 0;
+                       int rc; 
+               
+                       FDB->ChunkBuffer->BufUsed = sent;
+
+                       while (nWritten < FDB->ChunkBuffer->BufUsed) {
+                               rc =  write(FDB->OtherFD, FDB->ChunkBuffer->buf + nWritten, FDB->ChunkBuffer->BufUsed - nWritten);
+                               if (rc < 0) {
+                                       *Err = strerror(errno);
+                                       return rc;
+                               }
+                               nWritten += rc;
+
+                       }
+                       FDB->ChunkBuffer->BufUsed = 0;
+                       FDB->TotalSentAlready += sent;
+                       FDB->ChunkSendRemain -= sent;
+                       return FDB->ChunkSendRemain;
+               }
+               else if (sent < 0) {
+                       *Err = strerror(errno);
+                       return sent;
+               }
+               return 0;
+       }
 }
 
 eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error)
@@ -3946,6 +4205,11 @@ int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
 {
        int len, rlen, slen;
 
+       if ((buf == NULL) || (buf->buf == NULL)) {
+               *Error = strerror(EINVAL);
+               return -1;
+       }
+
        if (!append)
                FlushStrBuf(buf);
 
@@ -4289,7 +4553,7 @@ int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **E
        struct timeval tv;
        fd_set rfds;
 
-       if ((Buf == NULL) || (*fd == -1))
+       if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
        {
                *Error = ErrRBLF_BLOBPreConditionFailed;
                return -1;
@@ -4371,8 +4635,7 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
 {
        const char *pos;
        int fdflags;
-       int len = 0;
-       int rlen;
+       int rlen = 0;
        int nRead = 0;
        int nAlreadyRead = 0;
        int IsNonBlock;
@@ -4398,8 +4661,8 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
        pos = *Pos;
 
        if (pos != NULL)
-               len = pos - IOBuf->buf;
-       rlen = IOBuf->BufUsed - len;
+               rlen = pos - IOBuf->buf;
+       rlen = IOBuf->BufUsed - rlen;
 
 
        if ((IOBuf->BufUsed > 0) && 
@@ -4433,8 +4696,6 @@ int StrBufReadBLOBBuffered(StrBuf *Blob,
                IncreaseBuf(IOBuf, 0, nBytes - nRead);
        ptr = IOBuf->buf;
 
-       len = Blob->BufUsed;
-
        fdflags = fcntl(*fd, F_GETFL);
        IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
        if (IsNonBlock)
@@ -4531,7 +4792,11 @@ int StrBufSipLine(StrBuf *LineBuf, const StrBuf *Buf, const char **Ptr)
        const char *aptr, *ptr, *eptr;
        char *optr, *xptr;
 
-       if ((Buf == NULL) || (*Ptr == StrBufNOTNULL)) {
+       if ((Buf == NULL) ||
+           (*Ptr == StrBufNOTNULL) ||
+           (LineBuf == NULL)||
+           (LineBuf->buf == NULL))
+       {
                *Ptr = StrBufNOTNULL;
                return 0;
        }
@@ -4604,9 +4869,10 @@ void StrBufStripSlashes(StrBuf *Dir, int RemoveTrailingSlash)
                        b++; a++;
                }
        }
-       if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
-               *b = '/';
-               b++;
+       if ((RemoveTrailingSlash) &&
+           (b > Dir->buf) && 
+           (*(b - 1) == '/')){
+               b--;
        }
        *b = '\0';
        Dir->BufUsed = b - Dir->buf;