X-Git-Url: https://code.citadel.org/?a=blobdiff_plain;f=libcitadel%2Flib%2Fstringbuf.c;h=466bb2c32da704fe39e588782c7142fb8e7414c6;hb=a85c25a80a5aec5bc4d805473ccaa23cffc650c9;hp=cdb625cc99b86b03eaa6cafce09554529209f314;hpb=94429066c040cbbe31046e08dbd2068d433b4fb6;p=citadel.git diff --git a/libcitadel/lib/stringbuf.c b/libcitadel/lib/stringbuf.c index cdb625cc9..466bb2c32 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,10 @@ #include #define SHOW_ME_VAPPEND_PRINTF #include +#ifndef LINUX_SENDFILE +#include +#include +#endif #include "libcitadel.h" #ifdef HAVE_ICONV @@ -20,6 +43,10 @@ #include #endif +#ifdef LINUX_SENDFILE +#include +#endif + #ifdef HAVE_ZLIB #include int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen, @@ -2125,6 +2152,7 @@ long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn) const char *aptr, *eiptr; char *bptr, *eptr; long len; + int IsUtf8Sequence; if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) ) return -1; @@ -2144,31 +2172,83 @@ long StrECMAEscAppend(StrBuf *Target, const StrBuf *Source, const char *PlainIn) return -1; bptr = Target->buf + Target->BufUsed; - eptr = Target->buf + Target->BufSize - 3; /* our biggest unit to put in... */ + eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */ while (aptr < eiptr){ if(bptr >= eptr) { IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 3; + eptr = Target->buf + Target->BufSize - 7; /* our biggest unit to put in... */ bptr = Target->buf + Target->BufUsed; } - if (*aptr == '"') { + switch (*aptr) { + case '\n': + memcpy(bptr, HKEY("\\n")); + bptr += 2; + Target->BufUsed += 2; + break; + case '\r': + memcpy(bptr, HKEY("\\r")); + bptr += 2; + Target->BufUsed += 2; + break; + case '"': *bptr = '\\'; bptr ++; *bptr = '"'; bptr ++; Target->BufUsed += 2; - } else if (*aptr == '\\') { + break; + case '\\': + if ((*(aptr + 1) == 'u') && + isxdigit(*(aptr + 2)) && + isxdigit(*(aptr + 3)) && + isxdigit(*(aptr + 4)) && + isxdigit(*(aptr + 5))) + { /* oh, a unicode escaper. let it pass through. */ + memcpy(bptr, aptr, 6); + aptr += 5; + bptr +=6; + Target->BufUsed += 6; + } + else + { + *bptr = '\\'; + bptr ++; + *bptr = '\\'; + bptr ++; + Target->BufUsed += 2; + } + break; + case '\b': *bptr = '\\'; bptr ++; + *bptr = 'b'; + bptr ++; + Target->BufUsed += 2; + break; + case '\f': *bptr = '\\'; bptr ++; + *bptr = 'f'; + bptr ++; Target->BufUsed += 2; - } - else{ - *bptr = *aptr; - bptr++; - Target->BufUsed ++; + break; + case '\t': + *bptr = '\\'; + bptr ++; + *bptr = 't'; + bptr ++; + Target->BufUsed += 2; + break; + default: + IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr); + while (IsUtf8Sequence > 0){ + *bptr = *aptr; + Target->BufUsed ++; + if (--IsUtf8Sequence) + aptr++; + bptr++; + } } aptr ++; } @@ -2222,83 +2302,127 @@ long StrHtmlEcmaEscAppend(StrBuf *Target, const StrBuf *Source, const char *Plai eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ bptr = Target->buf + Target->BufUsed; } - if (*aptr == '<') { - memcpy(bptr, "<", 4); + switch (*aptr) { + case '<': + memcpy(bptr, HKEY("<")); bptr += 4; Target->BufUsed += 4; - } - else if (*aptr == '>') { - memcpy(bptr, ">", 4); + break; + case '>': + memcpy(bptr, HKEY(">")); bptr += 4; Target->BufUsed += 4; - } - else if (*aptr == '&') { - memcpy(bptr, "&", 5); + break; + case '&': + memcpy(bptr, HKEY("&")); bptr += 5; Target->BufUsed += 5; - } - else if (*aptr == LB) { + break; + case LB: *bptr = '<'; bptr ++; Target->BufUsed ++; - } - else if (*aptr == RB) { + break; + case RB: *bptr = '>'; bptr ++; Target->BufUsed ++; - } - else if ((*aptr == 32) && (nbsp == 1)) { - memcpy(bptr, " ", 6); - bptr += 6; - Target->BufUsed += 6; - } - else if ((*aptr == '\n') && (nolinebreaks == 1)) { - *bptr='\0'; /* nothing */ - } - else if ((*aptr == '\n') && (nolinebreaks == 2)) { - memcpy(bptr, "<br/>", 11); - bptr += 11; - Target->BufUsed += 11; - } - - else if ((*aptr == '\r') && (nolinebreaks != 0)) { - *bptr='\0'; /* nothing */ - } - - else if ((*aptr == '"') || (*aptr == QU)) { + break; + case '\n': + switch (nolinebreaks) { + case 1: + *bptr='\0'; /* nothing */ + break; + case 2: + memcpy(bptr, HKEY("<br/>")); + bptr += 11; + Target->BufUsed += 11; + break; + default: + memcpy(bptr, HKEY("\\n")); + bptr += 2; + Target->BufUsed += 2; + } + break; + case '\r': + switch (nolinebreaks) { + case 1: + case 2: + *bptr='\0'; /* nothing */ + break; + default: + memcpy(bptr, HKEY("\\r")); + bptr += 2; + Target->BufUsed += 2; + break; + } + break; + case '"': + case QU: *bptr = '\\'; bptr ++; *bptr = '"'; bptr ++; Target->BufUsed += 2; - } else if (*aptr == '\\') { + break; + case '\\': + if ((*(aptr + 1) == 'u') && + isxdigit(*(aptr + 2)) && + isxdigit(*(aptr + 3)) && + isxdigit(*(aptr + 4)) && + isxdigit(*(aptr + 5))) + { /* oh, a unicode escaper. let it pass through. */ + memcpy(bptr, aptr, 6); + aptr += 5; + bptr +=6; + Target->BufUsed += 6; + } + else + { + *bptr = '\\'; + bptr ++; + *bptr = '\\'; + bptr ++; + Target->BufUsed += 2; + } + break; + case '\b': *bptr = '\\'; bptr ++; + *bptr = 'b'; + bptr ++; + Target->BufUsed += 2; + break; + case '\f': *bptr = '\\'; bptr ++; + *bptr = 'f'; + bptr ++; Target->BufUsed += 2; - } - else { - if (((unsigned char)*aptr) >= 0x20) - { - IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr); - + break; + case '\t': + *bptr = '\\'; + bptr ++; + *bptr = 't'; + bptr ++; + Target->BufUsed += 2; + break; + case 32: + if (nbsp == 1) { + memcpy(bptr, HKEY(" ")); + bptr += 6; + Target->BufUsed += 6; + break; + } + default: + IsUtf8Sequence = Ctdl_GetUtf8SequenceLength(aptr, eiptr); + while (IsUtf8Sequence > 0){ *bptr = *aptr; Target->BufUsed ++; - while (IsUtf8Sequence > 1){ - if(bptr + IsUtf8Sequence >= eptr) { - IncreaseBuf(Target, 1, -1); - eptr = Target->buf + Target->BufSize - 11; /* our biggest unit to put in... */ - bptr = Target->buf + Target->BufUsed - 1; - } - bptr++; aptr++; - IsUtf8Sequence --; - *bptr = *aptr; - Target->BufUsed ++; - } + if (--IsUtf8Sequence) + aptr++; bptr++; } - } aptr ++; } @@ -2566,6 +2690,9 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source) if ((ch < 32) || (ch > 126) || (ch == 61) || + (ch == '=') || + (ch == '?') || + (ch == '_') || (ch == '[') || (ch == ']') ) { @@ -2573,7 +2700,10 @@ int StrBufRFC2047encode(StrBuf **target, const StrBuf *source) (*target)->BufUsed += 3; } else { - (*target)->buf[(*target)->BufUsed] = ch; + if (ch == ' ') + (*target)->buf[(*target)->BufUsed] = '_'; + else + (*target)->buf[(*target)->BufUsed] = ch; (*target)->BufUsed++; } } @@ -3211,19 +3341,24 @@ void StrBuf_RFC822_2_Utf8(StrBuf *Target, * @ingroup StrBuf * @brief evaluate the length of an utf8 special character sequence * @param Char the character to examine - * @returns width of utf8 chars in bytes + * @returns width of utf8 chars in bytes; if the sequence is broken 0 is returned; 1 if its simply ASCII. */ static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE) { - int n = 1; - char test = (1<<7); - - while ((n < 8) && ((test & *CharS) != 0)) { - test = test << 1; + int n = 0; + unsigned char test = (1<<7); + + if ((*CharS & 0xC0) != 0xC0) + return 1; + + while ((n < 8) && + ((test & ((unsigned char)*CharS)) != 0)) + { + test = test >> 1; n ++; } if ((n > 6) || ((CharE - CharS) < n)) - n = 1; + n = 0; return n; } @@ -3236,7 +3371,7 @@ static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *Char static inline int Ctdl_IsUtf8SequenceStart(const char Char) { /** 11??.???? indicates an UTF8 Sequence. */ - return ((Char & 0xC0) != 0); + return ((Char & 0xC0) == 0xC0); } /** @@ -3597,7 +3732,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; } } @@ -3637,6 +3783,153 @@ 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 = NewStrBuf(); +#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; + return FDB->ChunkSendRemain; +#else +#endif + return 0; +} + +int FileRecvChunked(FDIOBuffer *FDB, const char **Err) +{ + +#ifdef LINUX_SENDFILE + ssize_t sent, pipesize; + long foo = 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; + } + + 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 +#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! * *******************************************************************************/