Add support for sending / reading to files; so far we just implement the linux sendfi...
[citadel.git] / libcitadel / lib / stringbuf.c
index 044cecf0a65037c3f7d2fb9784c8c41913b4ef2e..08e904b315fc36f896823e6a9f0b174abf556a72 100644 (file)
@@ -1,3 +1,21 @@
+/*
+ * 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
+ */
+
 #include "sysdep.h"
 #include <ctype.h>
 #include <errno.h>
@@ -10,6 +28,9 @@
 #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
 #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,
@@ -3715,7 +3740,7 @@ eReadState StrBufChunkSipLine(StrBuf *LineBuf, IOBuffer *FB)
                                */
                                StrBufCutLeft(FB->Buf, 
                                              FB->ReadWritePointer - FB->Buf->buf);
-                               FB->ReadWritePointer = NULL;
+                               FB->ReadWritePointer = FB->Buf->buf;
                        }
                        return eMustReadMore;
                }
@@ -3756,6 +3781,125 @@ 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();
+#endif
+       FDB->OtherFD = FD;
+}
+
+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;
+       sent = sendfile(FDB->OtherFD, FDB->IOB->fd, &FDB->TotalSentAlready, FDB->ChunkSendRemain);
+       if (sent == -1)
+       {
+               *Err = strerror(errno);
+               return sent;
+       }
+       FDB->ChunkSendRemain -= sent;
+       return FDB->ChunkSendRemain;
+#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!                  *
  *******************************************************************************/