]> code.citadel.org Git - citadel.git/blobdiff - libcitadel/lib/stringbuf.c
* implement buffered IO for blob-reading, so you can continue siping from the blob...
[citadel.git] / libcitadel / lib / stringbuf.c
index f669ecf362ae895a76d078358a58e8fb9fda6ad6..7db6017d600028d35fd366bb37272740f0bd2f31 100644 (file)
@@ -332,10 +332,10 @@ long StrBufPeek(StrBuf *Buf, const char* ptr, long nThChar, char PeekValue)
  */
 void StrBufAppendBuf(StrBuf *Buf, const StrBuf *AppendBuf, unsigned long Offset)
 {
-  if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
+       if ((AppendBuf == NULL) || (Buf == NULL) || (AppendBuf->buf == NULL))
                return;
 
-       if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed)
+       if (Buf->BufSize - Offset < AppendBuf->BufUsed + Buf->BufUsed + 1)
                IncreaseBuf(Buf, 
                            (Buf->BufUsed > 0), 
                            AppendBuf->BufUsed + Buf->BufUsed);
@@ -584,13 +584,13 @@ void StrMsgEscAppend(StrBuf *Target, StrBuf *Source, const char *PlainIn)
        if (len == 0) 
                return;
 
-       eptr = Target->buf + Target->BufSize - 6
+       eptr = Target->buf + Target->BufSize - 8
        tptr = Target->buf + Target->BufUsed;
        
        while (aptr < eiptr){
                if(tptr >= eptr) {
                        IncreaseBuf(Target, 1, -1);
-                       eptr = Target->buf + Target->BufSize - 6
+                       eptr = Target->buf + Target->BufSize - 8
                        tptr = Target->buf + Target->BufUsed;
                }
               
@@ -649,12 +649,12 @@ long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn)
                return -1;
 
        bptr = Target->buf + Target->BufUsed;
-       eptr = Target->buf + Target->BufSize - 2; /* our biggest unit to put in...  */
+       eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in...  */
 
        while (aptr < eiptr){
                if(bptr >= eptr) {
                        IncreaseBuf(Target, 1, -1);
-                       eptr = Target->buf + Target->BufSize - 2
+                       eptr = Target->buf + Target->BufSize - 3
                        bptr = Target->buf + Target->BufUsed;
                }
                else if (*aptr == '"') {
@@ -697,7 +697,7 @@ int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t n
        }
        if (Offset + nChars < Source->BufUsed)
        {
-               if (nChars > dest->BufSize)
+               if (nChars >= dest->BufSize)
                        IncreaseBuf(dest, 0, nChars + 1);
                memcpy(dest->buf, Source->buf + Offset, nChars);
                dest->BufUsed = nChars;
@@ -705,7 +705,7 @@ int StrBufSub(StrBuf *dest, const StrBuf *Source, unsigned long Offset, size_t n
                return nChars;
        }
        NCharsRemain = Source->BufUsed - Offset;
-       if (NCharsRemain > dest->BufSize)
+       if (NCharsRemain  >= dest->BufSize)
                IncreaseBuf(dest, 0, NCharsRemain + 1);
        memcpy(dest->buf, Source->buf + Offset, NCharsRemain);
        dest->BufUsed = NCharsRemain;
@@ -806,7 +806,7 @@ void StrBufPrintf(StrBuf *Buf, const char *format, ...)
  * \param tok    Tokenizer char to count
  * \returns numbers of tokenizer chars found
  */
-inline int StrBufNum_tokens(const StrBuf *source, char tok)
+int StrBufNum_tokens(const StrBuf *source, char tok)
 {
        if (source == NULL)
                return 0;
@@ -936,6 +936,9 @@ int StrBufExtract_token(StrBuf *dest, const StrBuf *Source, int parmnum, char se
 }
 
 
+
+
+
 /**
  * \brief a string tokenizer to fetch an integer
  * \param dest Destination StringBuffer
@@ -952,6 +955,7 @@ int StrBufExtract_int(const StrBuf* Source, int parmnum, char separator)
        buf[0] = '\0';
        tmp.BufSize = 64;
        tmp.BufUsed = 0;
+       tmp.ConstBuf = 1;
        if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
                return(atoi(buf));
        else
@@ -974,6 +978,7 @@ long StrBufExtract_long(const StrBuf* Source, int parmnum, char separator)
        buf[0] = '\0';
        tmp.BufSize = 64;
        tmp.BufUsed = 0;
+       tmp.ConstBuf = 1;
        if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0)
                return(atoi(buf));
        else
@@ -998,6 +1003,7 @@ unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, cha
        buf[0] = '\0';
        tmp.BufSize = 64;
        tmp.BufUsed = 0;
+       tmp.ConstBuf = 1;
        if (StrBufExtract_token(&tmp, Source, parmnum, separator) > 0) {
                pnum = &buf[0];
                if (*pnum == '-')
@@ -1010,6 +1016,207 @@ unsigned long StrBufExtract_unsigned_long(const StrBuf* Source, int parmnum, cha
 
 
 
+/**
+ * \brief a string tokenizer
+ * \param dest Destination StringBuffer
+ * \param Source StringBuffer to read into
+ * \param pStart pointer to the end of the last token. Feed with NULL.
+ * \param separator tokenizer param
+ * \returns -1 if not found, else length of token.
+ */
+int StrBufExtract_NextToken(StrBuf *dest, const StrBuf *Source, const char **pStart, char separator)
+{
+       const char *s, *EndBuffer;      //* source * /
+       int len = 0;                    //* running total length of extracted string * /
+       int current_token = 0;          //* token currently being processed * /
+        
+       if (dest != NULL) {
+               dest->buf[0] = '\0';
+               dest->BufUsed = 0;
+       }
+       else
+               return(-1);
+
+       if ((Source == NULL) || 
+           (Source->BufUsed ==0)) {
+               return(-1);
+       }
+       if (*pStart == NULL)
+               *pStart = Source->buf;
+
+       EndBuffer = Source->buf + Source->BufUsed;
+
+       if ((*pStart < Source->buf) || 
+           (*pStart >  EndBuffer)) {
+               return (-1);
+       }
+
+
+       s = *pStart;
+
+       //cit_backtrace();
+       //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
+
+       while ((s<EndBuffer) && !IsEmptyStr(s)) {
+               if (*s == separator) {
+                       ++current_token;
+               }
+               if (len >= dest->BufSize)
+                       if (!IncreaseBuf(dest, 1, -1)) {
+                               *pStart = EndBuffer + 1;
+                               break;
+                       }
+               if ( (current_token == 0) && 
+                    (*s != separator)) {
+                       dest->buf[len] = *s;
+                       ++len;
+               }
+               else if (current_token > 0) {
+                       *pStart = s;
+                       break;
+               }
+               ++s;
+       }
+       *pStart = s;
+       (*pStart) ++;
+
+       dest->buf[len] = '\0';
+       dest->BufUsed = len;
+               //lprintf (CTDL_DEBUG,"test <!: %s\n", dest);
+       //lprintf (CTDL_DEBUG,"test <: %d; %s\n", len, dest);
+       return(len);
+}
+
+
+/**
+ * \brief a string tokenizer
+ * \param dest Destination StringBuffer
+ * \param Source StringBuffer to read into
+ * \param pStart pointer to the end of the last token. Feed with NULL.
+ * \param separator tokenizer param
+ * \returns -1 if not found, else length of token.
+ */
+int StrBufSkip_NTokenS(const StrBuf *Source, const char **pStart, char separator, int nTokens)
+{
+       const char *s, *EndBuffer;      //* source * /
+       int len = 0;                    //* running total length of extracted string * /
+       int current_token = 0;          //* token currently being processed * /
+
+       if ((Source == NULL) || 
+           (Source->BufUsed ==0)) {
+               return(-1);
+       }
+       if (nTokens == 0)
+               return Source->BufUsed;
+
+       if (*pStart == NULL)
+               *pStart = Source->buf;
+
+       EndBuffer = Source->buf + Source->BufUsed;
+
+       if ((*pStart < Source->buf) || 
+           (*pStart >  EndBuffer)) {
+               return (-1);
+       }
+
+
+       s = *pStart;
+
+       //cit_backtrace();
+       //lprintf (CTDL_DEBUG, "test >: n: %d sep: %c source: %s \n willi \n", parmnum, separator, source);
+
+       while ((s<EndBuffer) && !IsEmptyStr(s)) {
+               if (*s == separator) {
+                       ++current_token;
+               }
+               if (current_token >= nTokens) {
+                       break;
+               }
+               ++s;
+       }
+       *pStart = s;
+       (*pStart) ++;
+
+       return(len);
+}
+
+/**
+ * \brief a string tokenizer to fetch an integer
+ * \param dest Destination StringBuffer
+ * \param parmnum n'th parameter to extract
+ * \param separator tokenizer param
+ * \returns 0 if not found, else integer representation of the token
+ */
+int StrBufExtractNext_int(const StrBuf* Source, const char **pStart, char separator)
+{
+       StrBuf tmp;
+       char buf[64];
+       
+       tmp.buf = buf;
+       buf[0] = '\0';
+       tmp.BufSize = 64;
+       tmp.BufUsed = 0;
+       tmp.ConstBuf = 1;
+       if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
+               return(atoi(buf));
+       else
+               return 0;
+}
+
+/**
+ * \brief a string tokenizer to fetch a long integer
+ * \param dest Destination StringBuffer
+ * \param parmnum n'th parameter to extract
+ * \param separator tokenizer param
+ * \returns 0 if not found, else long integer representation of the token
+ */
+long StrBufExtractNext_long(const StrBuf* Source, const char **pStart, char separator)
+{
+       StrBuf tmp;
+       char buf[64];
+       
+       tmp.buf = buf;
+       buf[0] = '\0';
+       tmp.BufSize = 64;
+       tmp.BufUsed = 0;
+       tmp.ConstBuf = 1;
+       if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0)
+               return(atoi(buf));
+       else
+               return 0;
+}
+
+
+/**
+ * \brief a string tokenizer to fetch an unsigned long
+ * \param dest Destination StringBuffer
+ * \param parmnum n'th parameter to extract
+ * \param separator tokenizer param
+ * \returns 0 if not found, else unsigned long representation of the token
+ */
+unsigned long StrBufExtractNext_unsigned_long(const StrBuf* Source, const char **pStart, char separator)
+{
+       StrBuf tmp;
+       char buf[64];
+       char *pnum;
+       
+       tmp.buf = buf;
+       buf[0] = '\0';
+       tmp.BufSize = 64;
+       tmp.BufUsed = 0;
+       tmp.ConstBuf = 1;
+       if (StrBufExtract_NextToken(&tmp, Source, pStart, separator) > 0) {
+               pnum = &buf[0];
+               if (*pnum == '-')
+                       pnum ++;
+               return (unsigned long) atol(pnum);
+       }
+       else 
+               return 0;
+}
+
+
+
 /**
  * \brief Read a line from socket
  * flushes and closes the FD on error
@@ -1041,7 +1248,7 @@ int StrBufTCP_read_line(StrBuf *buf, int *fd, int append, const char **Error)
                        break;
                if (buf->buf[len] != '\r')
                        len ++;
-               if (!(len < buf->BufSize)) {
+               if (len >= buf->BufSize) {
                        buf->BufUsed = len;
                        buf->buf[len+1] = '\0';
                        IncreaseBuf(buf, 1, -1);
@@ -1143,6 +1350,107 @@ int StrBufTCP_read_buffered_line(StrBuf *Line,
 
 }
 
+/**
+ * \brief Read a line from socket
+ * flushes and closes the FD on error
+ * \param buf the buffer to get the input to
+ * \param Pos pointer to the current read position, should be NULL initialized!
+ * \param fd pointer to the filedescriptor to read
+ * \param append Append to an existing string or replace?
+ * \param Error strerror() on error 
+ * \returns numbers of chars read
+ */
+int StrBufTCP_read_buffered_line_fast(StrBuf *Line, 
+                                     StrBuf *buf, 
+                                     const char **Pos,
+                                     int *fd, 
+                                     int timeout, 
+                                     int selectresolution, 
+                                     const char **Error)
+{
+       int len, rlen;
+       int nSuccessLess = 0;
+       fd_set rfds;
+       const char *pch = NULL;
+        int fdflags;
+       struct timeval tv;
+
+       if ((buf->BufUsed > 0) && (Pos != NULL)) {
+               if (*Pos == NULL)
+                       *Pos = buf->buf;
+               pch = strchr(*Pos, '\n');
+               if (pch != NULL) {
+                       rlen = 0;
+                       len = pch - *Pos;
+                       if (len > 0 && (*(pch - 1) == '\r') )
+                               rlen ++;
+                       StrBufSub(Line, buf, (*Pos - buf->buf), len - rlen);
+                       *Pos = pch + 1;
+                       return len - rlen;
+               }
+       }
+       
+       if (*Pos != NULL) {
+               StrBufCutLeft(buf, (*Pos - buf->buf));
+               *Pos = NULL;
+       }
+       
+       if (buf->BufSize - buf->BufUsed < 10)
+               IncreaseBuf(buf, 1, -1);
+
+       fdflags = fcntl(*fd, F_GETFL);
+       if ((fdflags & O_NONBLOCK) == O_NONBLOCK)
+               return -1;
+
+       while ((nSuccessLess < timeout) && (pch == NULL)) {
+               tv.tv_sec = selectresolution;
+               tv.tv_usec = 0;
+               
+               FD_ZERO(&rfds);
+               FD_SET(*fd, &rfds);
+               if (select(*fd + 1, NULL, &rfds, NULL, &tv) == -1) {
+                       *Error = strerror(errno);
+                       close (*fd);
+                       *fd = -1;
+                       return -1;
+               }               
+               if (FD_ISSET(*fd, &rfds)) {
+                       rlen = read(*fd, 
+                                   &buf->buf[buf->BufUsed], 
+                                   buf->BufSize - buf->BufUsed - 1);
+                       if (rlen < 1) {
+                               *Error = strerror(errno);
+                               close(*fd);
+                               *fd = -1;
+                               return -1;
+                       }
+                       else if (rlen > 0) {
+                               nSuccessLess = 0;
+                               buf->BufUsed += rlen;
+                               buf->buf[buf->BufUsed] = '\0';
+                               if (buf->BufUsed + 10 > buf->BufSize) {
+                                       IncreaseBuf(buf, 1, -1);
+                               }
+                               pch = strchr(buf->buf, '\n');
+                               continue;
+                       }
+               }
+               nSuccessLess ++;
+       }
+       if (pch != NULL) {
+               *Pos = buf->buf;
+               rlen = 0;
+               len = pch - *Pos;
+               if (len > 0 && (*(pch - 1) == '\r') )
+                       rlen ++;
+               StrBufSub(Line, buf, 0, len - rlen);
+               *Pos = *Pos + len + 1;
+               return len - rlen;
+       }
+       return -1;
+
+}
+
 /**
  * \brief Input binary data from socket
  * flushes and closes the FD on error
@@ -1165,9 +1473,96 @@ int StrBufReadBLOB(StrBuf *Buf, int *fd, int append, long nBytes, const char **E
                return -1;
        if (!append)
                FlushStrBuf(Buf);
-       if (Buf->BufUsed + nBytes > Buf->BufSize)
+       if (Buf->BufUsed + nBytes >= Buf->BufSize)
+               IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
+
+       ptr = Buf->buf + Buf->BufUsed;
+
+       slen = len = Buf->BufUsed;
+
+       fdflags = fcntl(*fd, F_GETFL);
+
+       while (nRead < nBytes) {
+               if ((fdflags & O_NONBLOCK) == O_NONBLOCK) {
+                        FD_ZERO(&wset);
+                        FD_SET(*fd, &wset);
+                        if (select(*fd + 1, NULL, &wset, NULL, NULL) == -1) {
+                               *Error = strerror(errno);
+                                return -1;
+                        }
+                }
+
+                if ((rlen = read(*fd, 
+                                ptr,
+                                nBytes - nRead)) == -1) {
+                       close(*fd);
+                       *fd = -1;
+                       *Error = strerror(errno);
+                        return rlen;
+                }
+               nRead += rlen;
+               ptr += rlen;
+               Buf->BufUsed += rlen;
+       }
+       Buf->buf[Buf->BufUsed] = '\0';
+       return nRead;
+}
+
+/**
+ * \brief Input binary data from socket
+ * flushes and closes the FD on error
+ * \param buf the buffer to get the input to
+ * \param fd pointer to the filedescriptor to read
+ * \param append Append to an existing string or replace?
+ * \param nBytes the maximal number of bytes to read
+ * \param Error strerror() on error 
+ * \returns numbers of chars read
+ */
+int StrBufReadBLOBBuffered(StrBuf *Buf, 
+                          StrBuf *IOBuf, 
+                          const char **BufPos,
+                          int *fd, 
+                          int append, 
+                          long nBytes, 
+                          const char **Error)
+{
+        fd_set wset;
+        int fdflags;
+       int len, rlen, slen;
+       int nRead;
+       char *ptr;
+
+       if ((Buf == NULL) || (*fd == -1) || (IOBuf == NULL))
+               return -1;
+       if (!append)
+               FlushStrBuf(Buf);
+       if (Buf->BufUsed + nBytes >= Buf->BufSize)
                IncreaseBuf(Buf, 1, Buf->BufUsed + nBytes);
 
+
+       len = *BufPos - IOBuf->buf;
+       rlen = IOBuf->BufUsed - len;
+
+       if ((IOBuf->BufUsed > 0) && 
+           ((IOBuf->BufUsed - len > 0))) {
+               if (rlen <= nBytes) {
+                       memcpy(Buf->buf + Buf->BufUsed, *BufPos, rlen);
+                       Buf->BufUsed += rlen;
+                       Buf->buf[Buf->BufUsed] = '\0';
+                       *BufPos = NULL; FlushStrBuf(IOBuf);
+                       nRead = rlen;
+                       if (nRead == nBytes)
+                               return nBytes;
+               }
+               else {
+                       memcpy(Buf->buf + Buf->BufUsed, *BufPos, nBytes);
+                       Buf->BufUsed += nBytes;
+                       Buf->buf[Buf->BufUsed] = '\0';
+                       *BufPos += nBytes;
+                       return nBytes;
+               }
+       }
+
        ptr = Buf->buf + Buf->BufUsed;
 
        slen = len = Buf->BufUsed;
@@ -1499,6 +1894,25 @@ int StrBufDecodeBase64(StrBuf *Buf)
        return siz;
 }
 
+/**
+ * \brief replace all chars >0x20 && < 0x7F with Mute
+ * \param Mute char to put over invalid chars
+ * \param Buf Buffor to transform
+ */
+int StrBufSanitizeAscii(StrBuf *Buf, const char Mute)
+{
+       unsigned char *pch;
+
+       if (Buf == NULL) return -1;
+       pch = (unsigned char *)Buf->buf;
+       while (pch < (unsigned char *)Buf->buf + Buf->BufUsed) {
+               if ((*pch < 0x20) || (*pch > 0x7F))
+                       *pch = Mute;
+               pch ++;
+       }
+       return Buf->BufUsed;
+}
+
 
 /**
  * \brief  remove escaped strings from i.e. the url string (like %20 for blanks)
@@ -1588,12 +2002,12 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
        }
        if (*target == NULL)
                *target = NewStrBufPlain(NULL, sizeof(headerStr) + source->BufUsed * 2);
-       else if (sizeof(headerStr) + source->BufUsed > (*target)->BufSize)
+       else if (sizeof(headerStr) + source->BufUsed >= (*target)->BufSize)
                IncreaseBuf(*target, sizeof(headerStr) + source->BufUsed, 0);
        memcpy ((*target)->buf, headerStr, sizeof(headerStr) - 1);
        (*target)->BufUsed = sizeof(headerStr) - 1;
        for (i=0; (i < source->BufUsed); ++i) {
-               if ((*target)->BufUsed + 4 > (*target)->BufSize)
+               if ((*target)->BufUsed + 4 >= (*target)->BufSize)
                        IncreaseBuf(*target, 1, 0);
                ch = (unsigned char) source->buf[i];
                if ((ch < 32) || (ch > 126) || (ch == 61)) {
@@ -1606,7 +2020,7 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source)
                }
        }
        
-       if ((*target)->BufUsed + 4 > (*target)->BufSize)
+       if ((*target)->BufUsed + 4 >= (*target)->BufSize)
                IncreaseBuf(*target, 1, 0);
 
        (*target)->buf[(*target)->BufUsed++] = '?';
@@ -1698,7 +2112,7 @@ void StrBufConvert(StrBuf *ConvertBuf, StrBuf *TmpBuf, void *pic)
        size_t obuflen;                 /**< Length of output buffer */
 
 
-       if (ConvertBuf->BufUsed > TmpBuf->BufSize)
+       if (ConvertBuf->BufUsed >= TmpBuf->BufSize)
                IncreaseBuf(TmpBuf, 0, ConvertBuf->BufUsed);
 
        ic = *(iconv_t*)pic;
@@ -1964,7 +2378,7 @@ int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
 
        optr = LineBuf->buf;
        eptr = Buf->buf + Buf->BufUsed;
-       xptr = LineBuf->buf + LineBuf->BufSize;
+       xptr = LineBuf->buf + LineBuf->BufSize - 1;
 
        while ((*ptr != '\n') &&
               (*ptr != '\r') &&
@@ -1976,7 +2390,7 @@ int StrBufSipLine(StrBuf *LineBuf, StrBuf *Buf, const char **Ptr)
                        LineBuf->BufUsed = optr - LineBuf->buf;
                        IncreaseBuf(LineBuf,  1, LineBuf->BufUsed + 1);
                        optr = LineBuf->buf + LineBuf->BufUsed;
-                       xptr = LineBuf->buf + LineBuf->BufSize;
+                       xptr = LineBuf->buf + LineBuf->BufSize - 1;
                }
        }
        LineBuf->BufUsed = optr - LineBuf->buf;