mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-05-29 01:51:32 +00:00
git-svn-id: http://svn.freeswitch.org/svn/freeswitch/trunk@3693 d0543943-73ff-0310-b7d9-9358b9ac24b2
629 lines
17 KiB
C
629 lines
17 KiB
C
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is the Netscape Portable Runtime (NSPR).
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Netscape Communications Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 1998-2000
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
#include "primpl.h"
|
|
|
|
#include <string.h>
|
|
|
|
/*****************************************************************************/
|
|
/************************** Invalid I/O method object ************************/
|
|
/*****************************************************************************/
|
|
PRIOMethods _pr_faulty_methods = {
|
|
(PRDescType)0,
|
|
(PRCloseFN)_PR_InvalidStatus,
|
|
(PRReadFN)_PR_InvalidInt,
|
|
(PRWriteFN)_PR_InvalidInt,
|
|
(PRAvailableFN)_PR_InvalidInt,
|
|
(PRAvailable64FN)_PR_InvalidInt64,
|
|
(PRFsyncFN)_PR_InvalidStatus,
|
|
(PRSeekFN)_PR_InvalidInt,
|
|
(PRSeek64FN)_PR_InvalidInt64,
|
|
(PRFileInfoFN)_PR_InvalidStatus,
|
|
(PRFileInfo64FN)_PR_InvalidStatus,
|
|
(PRWritevFN)_PR_InvalidInt,
|
|
(PRConnectFN)_PR_InvalidStatus,
|
|
(PRAcceptFN)_PR_InvalidDesc,
|
|
(PRBindFN)_PR_InvalidStatus,
|
|
(PRListenFN)_PR_InvalidStatus,
|
|
(PRShutdownFN)_PR_InvalidStatus,
|
|
(PRRecvFN)_PR_InvalidInt,
|
|
(PRSendFN)_PR_InvalidInt,
|
|
(PRRecvfromFN)_PR_InvalidInt,
|
|
(PRSendtoFN)_PR_InvalidInt,
|
|
(PRPollFN)_PR_InvalidInt16,
|
|
(PRAcceptreadFN)_PR_InvalidInt,
|
|
(PRTransmitfileFN)_PR_InvalidInt,
|
|
(PRGetsocknameFN)_PR_InvalidStatus,
|
|
(PRGetpeernameFN)_PR_InvalidStatus,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRGetsocketoptionFN)_PR_InvalidStatus,
|
|
(PRSetsocketoptionFN)_PR_InvalidStatus,
|
|
(PRSendfileFN)_PR_InvalidInt,
|
|
(PRConnectcontinueFN)_PR_InvalidStatus,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRReservedFN)_PR_InvalidInt,
|
|
(PRReservedFN)_PR_InvalidInt
|
|
};
|
|
|
|
PRIntn _PR_InvalidInt(void)
|
|
{
|
|
PR_ASSERT(!"I/O method is invalid");
|
|
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
|
|
return -1;
|
|
} /* _PR_InvalidInt */
|
|
|
|
PRInt16 _PR_InvalidInt16(void)
|
|
{
|
|
PR_ASSERT(!"I/O method is invalid");
|
|
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
|
|
return -1;
|
|
} /* _PR_InvalidInt */
|
|
|
|
PRInt64 _PR_InvalidInt64(void)
|
|
{
|
|
PRInt64 rv;
|
|
LL_I2L(rv, -1);
|
|
PR_ASSERT(!"I/O method is invalid");
|
|
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
|
|
return rv;
|
|
} /* _PR_InvalidInt */
|
|
|
|
/*
|
|
* An invalid method that returns PRStatus
|
|
*/
|
|
|
|
PRStatus _PR_InvalidStatus(void)
|
|
{
|
|
PR_ASSERT(!"I/O method is invalid");
|
|
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
|
|
return PR_FAILURE;
|
|
} /* _PR_InvalidDesc */
|
|
|
|
/*
|
|
* An invalid method that returns a pointer
|
|
*/
|
|
|
|
PRFileDesc *_PR_InvalidDesc(void)
|
|
{
|
|
PR_ASSERT(!"I/O method is invalid");
|
|
PR_SetError(PR_INVALID_METHOD_ERROR, 0);
|
|
return NULL;
|
|
} /* _PR_InvalidDesc */
|
|
|
|
PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file)
|
|
{
|
|
return file->methods->file_type;
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd)
|
|
{
|
|
return (fd->methods->close)(fd);
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
|
|
{
|
|
return((fd->methods->read)(fd,buf,amount));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount)
|
|
{
|
|
return((fd->methods->write)(fd,buf,amount));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
|
|
{
|
|
return((fd->methods->seek)(fd, offset, whence));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
|
|
{
|
|
return((fd->methods->seek64)(fd, offset, whence));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd)
|
|
{
|
|
return((fd->methods->available)(fd));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd)
|
|
{
|
|
return((fd->methods->available64)(fd));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info)
|
|
{
|
|
return((fd->methods->fileInfo)(fd, info));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
|
|
{
|
|
return((fd->methods->fileInfo64)(fd, info));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd)
|
|
{
|
|
return((fd->methods->fsync)(fd));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_Connect(
|
|
PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
return((fd->methods->connect)(fd,addr,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_ConnectContinue(
|
|
PRFileDesc *fd, PRInt16 out_flags)
|
|
{
|
|
return((fd->methods->connectcontinue)(fd,out_flags));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr,
|
|
PRIntervalTime timeout)
|
|
{
|
|
return((fd->methods->accept)(fd,addr,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr)
|
|
{
|
|
return((fd->methods->bind)(fd,addr));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how)
|
|
{
|
|
return((fd->methods->shutdown)(fd,how));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog)
|
|
{
|
|
return((fd->methods->listen)(fd,backlog));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount,
|
|
PRIntn flags, PRIntervalTime timeout)
|
|
{
|
|
return((fd->methods->recv)(fd,buf,amount,flags,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount,
|
|
PRIntn flags, PRIntervalTime timeout)
|
|
{
|
|
return((fd->methods->send)(fd,buf,amount,flags,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, const PRIOVec *iov,
|
|
PRInt32 iov_size, PRIntervalTime timeout)
|
|
{
|
|
if (iov_size > PR_MAX_IOVECTOR_SIZE)
|
|
{
|
|
PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
|
|
return -1;
|
|
}
|
|
return((fd->methods->writev)(fd,iov,iov_size,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
|
|
PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_SendTo(
|
|
PRFileDesc *fd, const void *buf, PRInt32 amount,
|
|
PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
|
|
{
|
|
return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_TransmitFile(
|
|
PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen,
|
|
PRTransmitFileFlags flags, PRIntervalTime timeout)
|
|
{
|
|
return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_AcceptRead(
|
|
PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
|
|
void *buf, PRInt32 amount, PRIntervalTime timeout)
|
|
{
|
|
return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr)
|
|
{
|
|
return((fd->methods->getsockname)(fd,addr));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
|
|
{
|
|
return((fd->methods->getpeername)(fd,addr));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_GetSocketOption(
|
|
PRFileDesc *fd, PRSocketOptionData *data)
|
|
{
|
|
return((fd->methods->getsocketoption)(fd, data));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRStatus) PR_SetSocketOption(
|
|
PRFileDesc *fd, const PRSocketOptionData *data)
|
|
{
|
|
return((fd->methods->setsocketoption)(fd, data));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_SendFile(
|
|
PRFileDesc *sd, PRSendFileData *sfd,
|
|
PRTransmitFileFlags flags, PRIntervalTime timeout)
|
|
{
|
|
return((sd->methods->sendfile)(sd,sfd,flags,timeout));
|
|
}
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_EmulateAcceptRead(
|
|
PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
|
|
void *buf, PRInt32 amount, PRIntervalTime timeout)
|
|
{
|
|
PRInt32 rv = -1;
|
|
PRNetAddr remote;
|
|
PRFileDesc *accepted = NULL;
|
|
|
|
/*
|
|
** The timeout does not apply to the accept portion of the
|
|
** operation - it waits indefinitely.
|
|
*/
|
|
accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT);
|
|
if (NULL == accepted) return rv;
|
|
|
|
rv = PR_Recv(accepted, buf, amount, 0, timeout);
|
|
if (rv >= 0)
|
|
{
|
|
/* copy the new info out where caller can see it */
|
|
#define AMASK ((PRPtrdiff)7) /* mask for alignment of PRNetAddr */
|
|
PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK;
|
|
*raddr = (PRNetAddr*)(aligned & ~AMASK);
|
|
memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
|
|
*nd = accepted;
|
|
return rv;
|
|
}
|
|
|
|
PR_Close(accepted);
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* PR_EmulateSendFile
|
|
*
|
|
* Send file sfd->fd across socket sd. If header/trailer are specified
|
|
* they are sent before and after the file, respectively.
|
|
*
|
|
* PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
|
|
*
|
|
* return number of bytes sent or -1 on error
|
|
*
|
|
*/
|
|
|
|
#if defined(XP_UNIX) || defined(WIN32)
|
|
|
|
/*
|
|
* An implementation based on memory-mapped files
|
|
*/
|
|
|
|
#define SENDFILE_MMAP_CHUNK (256 * 1024)
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
|
|
PRFileDesc *sd, PRSendFileData *sfd,
|
|
PRTransmitFileFlags flags, PRIntervalTime timeout)
|
|
{
|
|
PRInt32 rv, count = 0;
|
|
PRInt32 len, file_bytes, index = 0;
|
|
PRFileInfo info;
|
|
PRIOVec iov[3];
|
|
PRFileMap *mapHandle = NULL;
|
|
void *addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler warnings down. */
|
|
PRUint32 file_mmap_offset, alignment;
|
|
PRInt64 zero64;
|
|
PROffset64 file_mmap_offset64;
|
|
PRUint32 addr_offset, mmap_len;
|
|
|
|
/* Get file size */
|
|
if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) {
|
|
count = -1;
|
|
goto done;
|
|
}
|
|
if (sfd->file_nbytes &&
|
|
(info.size < (sfd->file_offset + sfd->file_nbytes))) {
|
|
/*
|
|
* there are fewer bytes in file to send than specified
|
|
*/
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
count = -1;
|
|
goto done;
|
|
}
|
|
if (sfd->file_nbytes)
|
|
file_bytes = sfd->file_nbytes;
|
|
else
|
|
file_bytes = info.size - sfd->file_offset;
|
|
|
|
alignment = PR_GetMemMapAlignment();
|
|
|
|
/* number of initial bytes to skip in mmap'd segment */
|
|
addr_offset = sfd->file_offset % alignment;
|
|
|
|
/* find previous mmap alignment boundary */
|
|
file_mmap_offset = sfd->file_offset - addr_offset;
|
|
|
|
/*
|
|
* If the file is large, mmap and send the file in chunks so as
|
|
* to not consume too much virtual address space
|
|
*/
|
|
mmap_len = PR_MIN(file_bytes + addr_offset, SENDFILE_MMAP_CHUNK);
|
|
len = mmap_len - addr_offset;
|
|
|
|
/*
|
|
* Map in (part of) file. Take care of zero-length files.
|
|
*/
|
|
if (len) {
|
|
LL_I2L(zero64, 0);
|
|
mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY);
|
|
if (!mapHandle) {
|
|
count = -1;
|
|
goto done;
|
|
}
|
|
LL_I2L(file_mmap_offset64, file_mmap_offset);
|
|
addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len);
|
|
if (!addr) {
|
|
count = -1;
|
|
goto done;
|
|
}
|
|
}
|
|
/*
|
|
* send headers first, followed by the file
|
|
*/
|
|
if (sfd->hlen) {
|
|
iov[index].iov_base = (char *) sfd->header;
|
|
iov[index].iov_len = sfd->hlen;
|
|
index++;
|
|
}
|
|
if (len) {
|
|
iov[index].iov_base = (char*)addr + addr_offset;
|
|
iov[index].iov_len = len;
|
|
index++;
|
|
}
|
|
if ((file_bytes == len) && (sfd->tlen)) {
|
|
/*
|
|
* all file data is mapped in; send the trailer too
|
|
*/
|
|
iov[index].iov_base = (char *) sfd->trailer;
|
|
iov[index].iov_len = sfd->tlen;
|
|
index++;
|
|
}
|
|
rv = PR_Writev(sd, iov, index, timeout);
|
|
if (len)
|
|
PR_MemUnmap(addr, mmap_len);
|
|
if (rv < 0) {
|
|
count = -1;
|
|
goto done;
|
|
}
|
|
|
|
PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0));
|
|
|
|
file_bytes -= len;
|
|
count += rv;
|
|
if (!file_bytes) /* header, file and trailer are sent */
|
|
goto done;
|
|
|
|
/*
|
|
* send remaining bytes of the file, if any
|
|
*/
|
|
len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
|
|
while (len > 0) {
|
|
/*
|
|
* Map in (part of) file
|
|
*/
|
|
file_mmap_offset = sfd->file_offset + count - sfd->hlen;
|
|
PR_ASSERT((file_mmap_offset % alignment) == 0);
|
|
|
|
LL_I2L(file_mmap_offset64, file_mmap_offset);
|
|
addr = PR_MemMap(mapHandle, file_mmap_offset64, len);
|
|
if (!addr) {
|
|
count = -1;
|
|
goto done;
|
|
}
|
|
rv = PR_Send(sd, addr, len, 0, timeout);
|
|
PR_MemUnmap(addr, len);
|
|
if (rv < 0) {
|
|
count = -1;
|
|
goto done;
|
|
}
|
|
|
|
PR_ASSERT(rv == len);
|
|
file_bytes -= rv;
|
|
count += rv;
|
|
len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
|
|
}
|
|
PR_ASSERT(0 == file_bytes);
|
|
if (sfd->tlen) {
|
|
rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout);
|
|
if (rv >= 0) {
|
|
PR_ASSERT(rv == sfd->tlen);
|
|
count += rv;
|
|
} else
|
|
count = -1;
|
|
}
|
|
done:
|
|
if (mapHandle)
|
|
PR_CloseFileMap(mapHandle);
|
|
if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET))
|
|
PR_Close(sd);
|
|
return count;
|
|
}
|
|
|
|
#else
|
|
|
|
PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
|
|
PRFileDesc *sd, PRSendFileData *sfd,
|
|
PRTransmitFileFlags flags, PRIntervalTime timeout)
|
|
{
|
|
PRInt32 rv, count = 0;
|
|
PRInt32 rlen;
|
|
const void * buffer;
|
|
PRInt32 buflen;
|
|
PRInt32 sendbytes, readbytes;
|
|
char *buf;
|
|
|
|
#define _SENDFILE_BUFSIZE (16 * 1024)
|
|
|
|
buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE);
|
|
if (buf == NULL) {
|
|
PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* send header first
|
|
*/
|
|
buflen = sfd->hlen;
|
|
buffer = sfd->header;
|
|
while (buflen) {
|
|
rv = PR_Send(sd, buffer, buflen, 0, timeout);
|
|
if (rv < 0) {
|
|
/* PR_Send() has invoked PR_SetError(). */
|
|
rv = -1;
|
|
goto done;
|
|
} else {
|
|
count += rv;
|
|
buffer = (const void*) ((const char*)buffer + rv);
|
|
buflen -= rv;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* send file next
|
|
*/
|
|
if (PR_Seek(sfd->fd, sfd->file_offset, PR_SEEK_SET) < 0) {
|
|
rv = -1;
|
|
goto done;
|
|
}
|
|
sendbytes = sfd->file_nbytes;
|
|
if (sendbytes == 0) {
|
|
/* send entire file */
|
|
while ((rlen = PR_Read(sfd->fd, buf, _SENDFILE_BUFSIZE)) > 0) {
|
|
while (rlen) {
|
|
char *bufptr = buf;
|
|
|
|
rv = PR_Send(sd, bufptr, rlen, 0, timeout);
|
|
if (rv < 0) {
|
|
/* PR_Send() has invoked PR_SetError(). */
|
|
rv = -1;
|
|
goto done;
|
|
} else {
|
|
count += rv;
|
|
bufptr = ((char*)bufptr + rv);
|
|
rlen -= rv;
|
|
}
|
|
}
|
|
}
|
|
if (rlen < 0) {
|
|
/* PR_Read() has invoked PR_SetError(). */
|
|
rv = -1;
|
|
goto done;
|
|
}
|
|
} else {
|
|
readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
|
|
while (readbytes && ((rlen = PR_Read(sfd->fd, buf, readbytes)) > 0)) {
|
|
while (rlen) {
|
|
char *bufptr = buf;
|
|
|
|
rv = PR_Send(sd, bufptr, rlen, 0, timeout);
|
|
if (rv < 0) {
|
|
/* PR_Send() has invoked PR_SetError(). */
|
|
rv = -1;
|
|
goto done;
|
|
} else {
|
|
count += rv;
|
|
sendbytes -= rv;
|
|
bufptr = ((char*)bufptr + rv);
|
|
rlen -= rv;
|
|
}
|
|
}
|
|
readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
|
|
}
|
|
if (rlen < 0) {
|
|
/* PR_Read() has invoked PR_SetError(). */
|
|
rv = -1;
|
|
goto done;
|
|
} else if (sendbytes != 0) {
|
|
/*
|
|
* there are fewer bytes in file to send than specified
|
|
*/
|
|
PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
|
|
rv = -1;
|
|
goto done;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* send trailer last
|
|
*/
|
|
buflen = sfd->tlen;
|
|
buffer = sfd->trailer;
|
|
while (buflen) {
|
|
rv = PR_Send(sd, buffer, buflen, 0, timeout);
|
|
if (rv < 0) {
|
|
/* PR_Send() has invoked PR_SetError(). */
|
|
rv = -1;
|
|
goto done;
|
|
} else {
|
|
count += rv;
|
|
buffer = (const void*) ((const char*)buffer + rv);
|
|
buflen -= rv;
|
|
}
|
|
}
|
|
rv = count;
|
|
|
|
done:
|
|
if (buf)
|
|
PR_DELETE(buf);
|
|
if ((rv >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET))
|
|
PR_Close(sd);
|
|
return rv;
|
|
}
|
|
|
|
#endif
|
|
|
|
/* priometh.c */
|