+/*
+ * 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 <ctype.h>
#include <errno.h>
#include <sys/types.h>
#define SHOW_ME_VAPPEND_PRINTF
#include <stdarg.h>
+
#include "libcitadel.h"
#ifdef HAVE_ICONV
#include <execinfo.h>
#endif
+#ifdef LINUX_SENDFILE
+#include <sys/sendfile.h>
+#endif
+
#ifdef HAVE_ZLIB
#include <zlib.h>
int ZEXPORT compress_gzip(Bytef * dest, size_t * destLen,
if (Siz == 0)
{
+ free(NewBuf);
return NULL;
}
*/
int FlushStrBuf(StrBuf *buf)
{
- if (buf == NULL)
+ if ((buf == NULL) || (buf->buf == NULL))
return -1;
if (buf->ConstBuf)
return -1;
*/
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)
*/
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;
const char *pLeft;
const char *pRight;
- if (Buf == NULL)
+ if ((Buf == NULL) || (Buf->buf == NULL))
return;
pLeft = pBuff = Buf->buf;
while (pBuff != NULL) {
*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
const char *aptr, *eiptr;
char *bptr, *eptr;
long len;
+ int IsUtf8Sequence;
if (((Source == NULL) && (PlainIn == NULL)) || (Target == NULL) )
return -1;
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)
if (target != NULL)
FlushStrBuf(target);
- if (source == NULL ||target == NULL)
+ if ((source == NULL) || (target == NULL) || (target->buf == NULL))
{
return;
}
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);
(ch > 126) ||
(ch == 61) ||
(ch == '=') ||
+ (ch == '?') ||
+ (ch == '_') ||
(ch == '[') ||
(ch == ']') )
{
(*target)->BufUsed += 3;
}
else {
- (*target)->buf[(*target)->BufUsed] = ch;
+ if (ch == ' ')
+ (*target)->buf[(*target)->BufUsed] = '_';
+ else
+ (*target)->buf[(*target)->BufUsed] = ch;
(*target)->BufUsed++;
}
}
while ((pch != NULL) && (pch < pche))
{
while (isspace(*pch)) pch++;
- UserStart = UserEnd = EmailStart = EmailEnd = NULL;
+ UserEnd = EmailStart = EmailEnd = NULL;
if ((*pch == '"') || (*pch == '\'')) {
UserStart = pch + 1;
EmailStart ++;
if (UserStart >= UserEnd)
UserStart = UserEnd = NULL;
- At = strchr(EmailStart, '@');
}
else { /* this is a local recipient... no domain, just a realname */
EmailStart = UserStart;
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);
#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
* 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;
}
/* 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)
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
*/
static inline int Ctdl_GetUtf8SequenceLength(const char *CharS, const char *CharE)
{
- int n = 1;
- char test = (1<<7);
+ int n = 0;
+ unsigned char test = (1<<7);
- if ((*CharS & 0xC0) == 0)
+ if ((*CharS & 0xC0) != 0xC0)
return 1;
- while ((n < 8) && ((test & *CharS) != 0)) {
- test = test << 1;
+ while ((n < 8) &&
+ ((test & ((unsigned char)*CharS)) != 0))
+ {
+ test = test >> 1;
n ++;
}
if ((n > 6) || ((CharE - CharS) < n))
static inline int Ctdl_IsUtf8SequenceStart(const char Char)
{
/** 11??.???? indicates an UTF8 Sequence. */
- return ((Char & 0xC0) != 0);
+ return ((Char & 0xC0) == 0xC0);
}
/**
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;
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;
}
}
return eReadSuccess;
}
+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 = 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! *
*******************************************************************************/
{
int len, rlen, slen;
+ if ((buf == NULL) || (buf->buf == NULL)) {
+ *Error = strerror(EINVAL);
+ return -1;
+ }
+
if (!append)
FlushStrBuf(buf);
struct timeval tv;
fd_set rfds;
- if ((Buf == NULL) || (*fd == -1))
+ if ((Buf == NULL) || (Buf->buf == NULL) || (*fd == -1))
{
*Error = ErrRBLF_BLOBPreConditionFailed;
return -1;
{
const char *pos;
int fdflags;
- int len = 0;
- int rlen;
+ int rlen = 0;
int nRead = 0;
int nAlreadyRead = 0;
int IsNonBlock;
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) &&
IncreaseBuf(IOBuf, 0, nBytes - nRead);
ptr = IOBuf->buf;
- len = Blob->BufUsed;
-
fdflags = fcntl(*fd, F_GETFL);
IsNonBlock = (fdflags & O_NONBLOCK) == O_NONBLOCK;
if (IsNonBlock)
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;
}
b++; a++;
}
}
- if ((RemoveTrailingSlash) && (*(b - 1) != '/')){
- *b = '/';
- b++;
+ if ((RemoveTrailingSlash) &&
+ (b > Dir->buf) &&
+ (*(b - 1) == '/')){
+ b--;
}
*b = '\0';
Dir->BufUsed = b - Dir->buf;