X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=libcitadel%2Flib%2Fstringbuf.c;h=b5bcf22f7a24d11d4b452700e1f5bdb45bc7335f;hb=0513df2bd1d22c17b3f401a4926a057037c1b5bd;hp=2ee3a2534aca8384985482867e7a739918612ab9;hpb=e4ba5efd0289624d736b328572ea24ece490be77;p=citadel.git diff --git a/libcitadel/lib/stringbuf.c b/libcitadel/lib/stringbuf.c index 2ee3a2534..b5bcf22f7 100644 --- a/libcitadel/lib/stringbuf.c +++ b/libcitadel/lib/stringbuf.c @@ -1,3 +1,22 @@ +/* + * Copyright (c) 1987-2011 by the citadel.org team + * + * This program is open source software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define _GNU_SOURCE #include "sysdep.h" #include #include @@ -10,6 +29,7 @@ #include #define SHOW_ME_VAPPEND_PRINTF #include + #include "libcitadel.h" #ifdef HAVE_ICONV @@ -20,6 +40,10 @@ #include #endif +#ifdef LINUX_SENDFILE +#include +#endif + #ifdef HAVE_ZLIB #include int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen, @@ -1119,7 +1143,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) { @@ -1797,6 +1821,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 @@ -2747,7 +2831,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; @@ -2818,7 +2902,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; @@ -3179,7 +3262,7 @@ 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; /* Sometimes, badly formed messages contain strings which were simply @@ -3189,7 +3272,6 @@ void StrBuf_RFC822_2_Utf8(StrBuf *Target, * charset to UTF-8 if we see any nonprintable characters. */ - len = StrLength(DecodeMe); for (i=0; iBufUsed; ++i) { if ((DecodeMe->buf[i] < 32) || (DecodeMe->buf[i] > 126)) { illegal_non_rfc2047_encoding = 1; @@ -3213,8 +3295,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) @@ -3231,7 +3312,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 @@ -3705,7 +3785,18 @@ eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB) optr --; if ((*(ptr - 1) != '\r') && (*(ptr - 1) != '\n')) { LineBuf->BufUsed = optr - LineBuf->buf; - *optr = '\0'; + *optr = '\0'; + if ((FB->ReadWritePointer != NULL) && + (FB->ReadWritePointer != FB->Buf->buf)) + { + /* Ok, the client application read all the data + it was interested in so far. Since there is more to read, + we now shrink the buffer, and move the rest over. + */ + StrBufCutLeft(FB->Buf, + FB->ReadWritePointer - FB->Buf->buf); + FB->ReadWritePointer = FB->Buf->buf; + } return eMustReadMore; } } @@ -3745,6 +3836,205 @@ eReadState StrBufCheckBuffer(IOBuffer *FB) return eReadSuccess; } +long IOBufferStrLength(IOBuffer *FB) +{ + 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 = NewStrBufPlain(NULL, TotalSendSize + 1); +#else + pipe(FDB->SplicePipe); +#endif + FDB->OtherFD = FD; +} + +void FDIOBufferDelete(FDIOBuffer *FDB) +{ +#ifndef LINUX_SENDFILE + FreeStrBuf(&FDB->ChunkBuffer); +#else + close(FDB->SplicePipe[0]); + close(FDB->SplicePipe[1]); + +#endif + 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) + { + *Err = strerror(errno); + return sent; + } + FDB->ChunkSendRemain -= sent; + FDB->TotalSentAlready += sent; + return FDB->ChunkSendRemain; +#else + + 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; + } +#endif +} + +int FileRecvChunked(FDIOBuffer *FDB, const char **Err) +{ + ssize_t sent, pipesize; + +#ifdef LINUX_SENDFILE + + 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; + } + + sent = splice(FDB->SplicePipe[0], NULL, + FDB->OtherFD, &FDB->TotalSentAlready, + pipesize, SPLICE_F_MORE | SPLICE_F_MOVE); + if (sent == -1) + { + *Err = strerror(errno); + return sent; + } + FDB->ChunkSendRemain -= sent; + return sent; +#else + + 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; + } + +#endif + return 0; +} + +eReadState WriteIOBAlreadyRead(FDIOBuffer *FDB, const char **Error) +{ + int IsNonBlock; + int fdflags; + long rlen; + long should_write; + int nSuccessLess = 0; + struct timeval tv; + fd_set rfds; + + fdflags = fcntl(FDB->OtherFD, F_GETFL); + IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK; + + while ((FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf < FDB->IOB->Buf->BufUsed) && + (FDB->ChunkSendRemain > 0)) + { + if (IsNonBlock){ + tv.tv_sec = 1; /* selectresolution; */ + tv.tv_usec = 0; + + FD_ZERO(&rfds); + FD_SET(FDB->OtherFD, &rfds); + if (select(FDB->OtherFD + 1, NULL, &rfds, NULL, &tv) == -1) { + *Error = strerror(errno); + return eReadFail; + } + } + if (IsNonBlock && ! FD_ISSET(FDB->OtherFD, &rfds)) { + nSuccessLess ++; + continue; + } + + should_write = FDB->IOB->Buf->BufUsed - + (FDB->IOB->ReadWritePointer - FDB->IOB->Buf->buf); + if (should_write > FDB->ChunkSendRemain) + should_write = FDB->ChunkSendRemain; + + rlen = write(FDB->OtherFD, + FDB->IOB->ReadWritePointer, + should_write); + if (rlen < 1) { + *Error = strerror(errno); + + return eReadFail; + } + FDB->TotalSentAlready += rlen; + FDB->IOB->ReadWritePointer += rlen; + FDB->ChunkSendRemain -= rlen; + } + if (FDB->IOB->ReadWritePointer >= FDB->IOB->Buf->buf + FDB->IOB->Buf->BufUsed) + { + FlushStrBuf(FDB->IOB->Buf); + FDB->IOB->ReadWritePointer = NULL; + } + + if (FDB->ChunkSendRemain == 0) + return eReadSuccess; + else + return eMustReadMore; +} + /******************************************************************************* * File I/O; Prefer buffered read since its faster! * *******************************************************************************/ @@ -4188,8 +4478,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; @@ -4215,8 +4504,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) && @@ -4250,8 +4539,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) @@ -4421,9 +4708,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;