michael@0: /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: #include "primpl.h" michael@0: michael@0: #include michael@0: michael@0: /*****************************************************************************/ michael@0: /************************** Invalid I/O method object ************************/ michael@0: /*****************************************************************************/ michael@0: PRIOMethods _pr_faulty_methods = { michael@0: (PRDescType)0, michael@0: (PRCloseFN)_PR_InvalidStatus, michael@0: (PRReadFN)_PR_InvalidInt, michael@0: (PRWriteFN)_PR_InvalidInt, michael@0: (PRAvailableFN)_PR_InvalidInt, michael@0: (PRAvailable64FN)_PR_InvalidInt64, michael@0: (PRFsyncFN)_PR_InvalidStatus, michael@0: (PRSeekFN)_PR_InvalidInt, michael@0: (PRSeek64FN)_PR_InvalidInt64, michael@0: (PRFileInfoFN)_PR_InvalidStatus, michael@0: (PRFileInfo64FN)_PR_InvalidStatus, michael@0: (PRWritevFN)_PR_InvalidInt, michael@0: (PRConnectFN)_PR_InvalidStatus, michael@0: (PRAcceptFN)_PR_InvalidDesc, michael@0: (PRBindFN)_PR_InvalidStatus, michael@0: (PRListenFN)_PR_InvalidStatus, michael@0: (PRShutdownFN)_PR_InvalidStatus, michael@0: (PRRecvFN)_PR_InvalidInt, michael@0: (PRSendFN)_PR_InvalidInt, michael@0: (PRRecvfromFN)_PR_InvalidInt, michael@0: (PRSendtoFN)_PR_InvalidInt, michael@0: (PRPollFN)_PR_InvalidInt16, michael@0: (PRAcceptreadFN)_PR_InvalidInt, michael@0: (PRTransmitfileFN)_PR_InvalidInt, michael@0: (PRGetsocknameFN)_PR_InvalidStatus, michael@0: (PRGetpeernameFN)_PR_InvalidStatus, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRGetsocketoptionFN)_PR_InvalidStatus, michael@0: (PRSetsocketoptionFN)_PR_InvalidStatus, michael@0: (PRSendfileFN)_PR_InvalidInt, michael@0: (PRConnectcontinueFN)_PR_InvalidStatus, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRReservedFN)_PR_InvalidInt, michael@0: (PRReservedFN)_PR_InvalidInt michael@0: }; michael@0: michael@0: PRIntn _PR_InvalidInt(void) michael@0: { michael@0: PR_ASSERT(!"I/O method is invalid"); michael@0: PR_SetError(PR_INVALID_METHOD_ERROR, 0); michael@0: return -1; michael@0: } /* _PR_InvalidInt */ michael@0: michael@0: PRInt16 _PR_InvalidInt16(void) michael@0: { michael@0: PR_ASSERT(!"I/O method is invalid"); michael@0: PR_SetError(PR_INVALID_METHOD_ERROR, 0); michael@0: return -1; michael@0: } /* _PR_InvalidInt */ michael@0: michael@0: PRInt64 _PR_InvalidInt64(void) michael@0: { michael@0: PRInt64 rv; michael@0: LL_I2L(rv, -1); michael@0: PR_ASSERT(!"I/O method is invalid"); michael@0: PR_SetError(PR_INVALID_METHOD_ERROR, 0); michael@0: return rv; michael@0: } /* _PR_InvalidInt */ michael@0: michael@0: /* michael@0: * An invalid method that returns PRStatus michael@0: */ michael@0: michael@0: PRStatus _PR_InvalidStatus(void) michael@0: { michael@0: PR_ASSERT(!"I/O method is invalid"); michael@0: PR_SetError(PR_INVALID_METHOD_ERROR, 0); michael@0: return PR_FAILURE; michael@0: } /* _PR_InvalidDesc */ michael@0: michael@0: /* michael@0: * An invalid method that returns a pointer michael@0: */ michael@0: michael@0: PRFileDesc *_PR_InvalidDesc(void) michael@0: { michael@0: PR_ASSERT(!"I/O method is invalid"); michael@0: PR_SetError(PR_INVALID_METHOD_ERROR, 0); michael@0: return NULL; michael@0: } /* _PR_InvalidDesc */ michael@0: michael@0: PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file) michael@0: { michael@0: return file->methods->file_type; michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd) michael@0: { michael@0: return (fd->methods->close)(fd); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount) michael@0: { michael@0: return((fd->methods->read)(fd,buf,amount)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount) michael@0: { michael@0: return((fd->methods->write)(fd,buf,amount)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence) michael@0: { michael@0: return((fd->methods->seek)(fd, offset, whence)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence) michael@0: { michael@0: return((fd->methods->seek64)(fd, offset, whence)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd) michael@0: { michael@0: return((fd->methods->available)(fd)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd) michael@0: { michael@0: return((fd->methods->available64)(fd)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info) michael@0: { michael@0: return((fd->methods->fileInfo)(fd, info)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info) michael@0: { michael@0: return((fd->methods->fileInfo64)(fd, info)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd) michael@0: { michael@0: return((fd->methods->fsync)(fd)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_Connect( michael@0: PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) michael@0: { michael@0: return((fd->methods->connect)(fd,addr,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_ConnectContinue( michael@0: PRFileDesc *fd, PRInt16 out_flags) michael@0: { michael@0: return((fd->methods->connectcontinue)(fd,out_flags)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr, michael@0: PRIntervalTime timeout) michael@0: { michael@0: return((fd->methods->accept)(fd,addr,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr) michael@0: { michael@0: return((fd->methods->bind)(fd,addr)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how) michael@0: { michael@0: return((fd->methods->shutdown)(fd,how)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog) michael@0: { michael@0: return((fd->methods->listen)(fd,backlog)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount, michael@0: PRIntn flags, PRIntervalTime timeout) michael@0: { michael@0: return((fd->methods->recv)(fd,buf,amount,flags,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount, michael@0: PRIntn flags, PRIntervalTime timeout) michael@0: { michael@0: return((fd->methods->send)(fd,buf,amount,flags,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, const PRIOVec *iov, michael@0: PRInt32 iov_size, PRIntervalTime timeout) michael@0: { michael@0: if (iov_size > PR_MAX_IOVECTOR_SIZE) michael@0: { michael@0: PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0); michael@0: return -1; michael@0: } michael@0: return((fd->methods->writev)(fd,iov,iov_size,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount, michael@0: PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout) michael@0: { michael@0: return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_SendTo( michael@0: PRFileDesc *fd, const void *buf, PRInt32 amount, michael@0: PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout) michael@0: { michael@0: return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_TransmitFile( michael@0: PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen, michael@0: PRTransmitFileFlags flags, PRIntervalTime timeout) michael@0: { michael@0: return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_AcceptRead( michael@0: PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, michael@0: void *buf, PRInt32 amount, PRIntervalTime timeout) michael@0: { michael@0: return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr) michael@0: { michael@0: return((fd->methods->getsockname)(fd,addr)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr) michael@0: { michael@0: return((fd->methods->getpeername)(fd,addr)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_GetSocketOption( michael@0: PRFileDesc *fd, PRSocketOptionData *data) michael@0: { michael@0: return((fd->methods->getsocketoption)(fd, data)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRStatus) PR_SetSocketOption( michael@0: PRFileDesc *fd, const PRSocketOptionData *data) michael@0: { michael@0: return((fd->methods->setsocketoption)(fd, data)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_SendFile( michael@0: PRFileDesc *sd, PRSendFileData *sfd, michael@0: PRTransmitFileFlags flags, PRIntervalTime timeout) michael@0: { michael@0: return((sd->methods->sendfile)(sd,sfd,flags,timeout)); michael@0: } michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_EmulateAcceptRead( michael@0: PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr, michael@0: void *buf, PRInt32 amount, PRIntervalTime timeout) michael@0: { michael@0: PRInt32 rv = -1; michael@0: PRNetAddr remote; michael@0: PRFileDesc *accepted = NULL; michael@0: michael@0: /* michael@0: ** The timeout does not apply to the accept portion of the michael@0: ** operation - it waits indefinitely. michael@0: */ michael@0: accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT); michael@0: if (NULL == accepted) return rv; michael@0: michael@0: rv = PR_Recv(accepted, buf, amount, 0, timeout); michael@0: if (rv >= 0) michael@0: { michael@0: /* copy the new info out where caller can see it */ michael@0: #define AMASK ((PRPtrdiff)7) /* mask for alignment of PRNetAddr */ michael@0: PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK; michael@0: *raddr = (PRNetAddr*)(aligned & ~AMASK); michael@0: memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote)); michael@0: *nd = accepted; michael@0: return rv; michael@0: } michael@0: michael@0: PR_Close(accepted); michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * PR_EmulateSendFile michael@0: * michael@0: * Send file sfd->fd across socket sd. If header/trailer are specified michael@0: * they are sent before and after the file, respectively. michael@0: * michael@0: * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file michael@0: * michael@0: * return number of bytes sent or -1 on error michael@0: * michael@0: */ michael@0: michael@0: #if defined(XP_UNIX) || defined(WIN32) michael@0: michael@0: /* michael@0: * An implementation based on memory-mapped files michael@0: */ michael@0: michael@0: #define SENDFILE_MMAP_CHUNK (256 * 1024) michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_EmulateSendFile( michael@0: PRFileDesc *sd, PRSendFileData *sfd, michael@0: PRTransmitFileFlags flags, PRIntervalTime timeout) michael@0: { michael@0: PRInt32 rv, count = 0; michael@0: PRInt32 len, file_bytes, index = 0; michael@0: PRFileInfo info; michael@0: PRIOVec iov[3]; michael@0: PRFileMap *mapHandle = NULL; michael@0: void *addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler warnings down. */ michael@0: PRUint32 file_mmap_offset, alignment; michael@0: PRInt64 zero64; michael@0: PROffset64 file_mmap_offset64; michael@0: PRUint32 addr_offset, mmap_len; michael@0: michael@0: /* Get file size */ michael@0: if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) { michael@0: count = -1; michael@0: goto done; michael@0: } michael@0: if (sfd->file_nbytes && michael@0: (info.size < (sfd->file_offset + sfd->file_nbytes))) { michael@0: /* michael@0: * there are fewer bytes in file to send than specified michael@0: */ michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); michael@0: count = -1; michael@0: goto done; michael@0: } michael@0: if (sfd->file_nbytes) michael@0: file_bytes = sfd->file_nbytes; michael@0: else michael@0: file_bytes = info.size - sfd->file_offset; michael@0: michael@0: alignment = PR_GetMemMapAlignment(); michael@0: michael@0: /* number of initial bytes to skip in mmap'd segment */ michael@0: addr_offset = sfd->file_offset % alignment; michael@0: michael@0: /* find previous mmap alignment boundary */ michael@0: file_mmap_offset = sfd->file_offset - addr_offset; michael@0: michael@0: /* michael@0: * If the file is large, mmap and send the file in chunks so as michael@0: * to not consume too much virtual address space michael@0: */ michael@0: mmap_len = PR_MIN(file_bytes + addr_offset, SENDFILE_MMAP_CHUNK); michael@0: len = mmap_len - addr_offset; michael@0: michael@0: /* michael@0: * Map in (part of) file. Take care of zero-length files. michael@0: */ michael@0: if (len) { michael@0: LL_I2L(zero64, 0); michael@0: mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY); michael@0: if (!mapHandle) { michael@0: count = -1; michael@0: goto done; michael@0: } michael@0: LL_I2L(file_mmap_offset64, file_mmap_offset); michael@0: addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len); michael@0: if (!addr) { michael@0: count = -1; michael@0: goto done; michael@0: } michael@0: } michael@0: /* michael@0: * send headers first, followed by the file michael@0: */ michael@0: if (sfd->hlen) { michael@0: iov[index].iov_base = (char *) sfd->header; michael@0: iov[index].iov_len = sfd->hlen; michael@0: index++; michael@0: } michael@0: if (len) { michael@0: iov[index].iov_base = (char*)addr + addr_offset; michael@0: iov[index].iov_len = len; michael@0: index++; michael@0: } michael@0: if ((file_bytes == len) && (sfd->tlen)) { michael@0: /* michael@0: * all file data is mapped in; send the trailer too michael@0: */ michael@0: iov[index].iov_base = (char *) sfd->trailer; michael@0: iov[index].iov_len = sfd->tlen; michael@0: index++; michael@0: } michael@0: rv = PR_Writev(sd, iov, index, timeout); michael@0: if (len) michael@0: PR_MemUnmap(addr, mmap_len); michael@0: if (rv < 0) { michael@0: count = -1; michael@0: goto done; michael@0: } michael@0: michael@0: PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0)); michael@0: michael@0: file_bytes -= len; michael@0: count += rv; michael@0: if (!file_bytes) /* header, file and trailer are sent */ michael@0: goto done; michael@0: michael@0: /* michael@0: * send remaining bytes of the file, if any michael@0: */ michael@0: len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK); michael@0: while (len > 0) { michael@0: /* michael@0: * Map in (part of) file michael@0: */ michael@0: file_mmap_offset = sfd->file_offset + count - sfd->hlen; michael@0: PR_ASSERT((file_mmap_offset % alignment) == 0); michael@0: michael@0: LL_I2L(file_mmap_offset64, file_mmap_offset); michael@0: addr = PR_MemMap(mapHandle, file_mmap_offset64, len); michael@0: if (!addr) { michael@0: count = -1; michael@0: goto done; michael@0: } michael@0: rv = PR_Send(sd, addr, len, 0, timeout); michael@0: PR_MemUnmap(addr, len); michael@0: if (rv < 0) { michael@0: count = -1; michael@0: goto done; michael@0: } michael@0: michael@0: PR_ASSERT(rv == len); michael@0: file_bytes -= rv; michael@0: count += rv; michael@0: len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK); michael@0: } michael@0: PR_ASSERT(0 == file_bytes); michael@0: if (sfd->tlen) { michael@0: rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout); michael@0: if (rv >= 0) { michael@0: PR_ASSERT(rv == sfd->tlen); michael@0: count += rv; michael@0: } else michael@0: count = -1; michael@0: } michael@0: done: michael@0: if (mapHandle) michael@0: PR_CloseFileMap(mapHandle); michael@0: if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) michael@0: PR_Close(sd); michael@0: return count; michael@0: } michael@0: michael@0: #else michael@0: michael@0: PR_IMPLEMENT(PRInt32) PR_EmulateSendFile( michael@0: PRFileDesc *sd, PRSendFileData *sfd, michael@0: PRTransmitFileFlags flags, PRIntervalTime timeout) michael@0: { michael@0: PRInt32 rv, count = 0; michael@0: PRInt32 rlen; michael@0: const void * buffer; michael@0: PRInt32 buflen; michael@0: PRInt32 sendbytes, readbytes; michael@0: char *buf; michael@0: michael@0: #define _SENDFILE_BUFSIZE (16 * 1024) michael@0: michael@0: buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE); michael@0: if (buf == NULL) { michael@0: PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0); michael@0: return -1; michael@0: } michael@0: michael@0: /* michael@0: * send header first michael@0: */ michael@0: buflen = sfd->hlen; michael@0: buffer = sfd->header; michael@0: while (buflen) { michael@0: rv = PR_Send(sd, buffer, buflen, 0, timeout); michael@0: if (rv < 0) { michael@0: /* PR_Send() has invoked PR_SetError(). */ michael@0: rv = -1; michael@0: goto done; michael@0: } else { michael@0: count += rv; michael@0: buffer = (const void*) ((const char*)buffer + rv); michael@0: buflen -= rv; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * send file next michael@0: */ michael@0: if (PR_Seek(sfd->fd, sfd->file_offset, PR_SEEK_SET) < 0) { michael@0: rv = -1; michael@0: goto done; michael@0: } michael@0: sendbytes = sfd->file_nbytes; michael@0: if (sendbytes == 0) { michael@0: /* send entire file */ michael@0: while ((rlen = PR_Read(sfd->fd, buf, _SENDFILE_BUFSIZE)) > 0) { michael@0: while (rlen) { michael@0: char *bufptr = buf; michael@0: michael@0: rv = PR_Send(sd, bufptr, rlen, 0, timeout); michael@0: if (rv < 0) { michael@0: /* PR_Send() has invoked PR_SetError(). */ michael@0: rv = -1; michael@0: goto done; michael@0: } else { michael@0: count += rv; michael@0: bufptr = ((char*)bufptr + rv); michael@0: rlen -= rv; michael@0: } michael@0: } michael@0: } michael@0: if (rlen < 0) { michael@0: /* PR_Read() has invoked PR_SetError(). */ michael@0: rv = -1; michael@0: goto done; michael@0: } michael@0: } else { michael@0: readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE); michael@0: while (readbytes && ((rlen = PR_Read(sfd->fd, buf, readbytes)) > 0)) { michael@0: while (rlen) { michael@0: char *bufptr = buf; michael@0: michael@0: rv = PR_Send(sd, bufptr, rlen, 0, timeout); michael@0: if (rv < 0) { michael@0: /* PR_Send() has invoked PR_SetError(). */ michael@0: rv = -1; michael@0: goto done; michael@0: } else { michael@0: count += rv; michael@0: sendbytes -= rv; michael@0: bufptr = ((char*)bufptr + rv); michael@0: rlen -= rv; michael@0: } michael@0: } michael@0: readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE); michael@0: } michael@0: if (rlen < 0) { michael@0: /* PR_Read() has invoked PR_SetError(). */ michael@0: rv = -1; michael@0: goto done; michael@0: } else if (sendbytes != 0) { michael@0: /* michael@0: * there are fewer bytes in file to send than specified michael@0: */ michael@0: PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0); michael@0: rv = -1; michael@0: goto done; michael@0: } michael@0: } michael@0: michael@0: /* michael@0: * send trailer last michael@0: */ michael@0: buflen = sfd->tlen; michael@0: buffer = sfd->trailer; michael@0: while (buflen) { michael@0: rv = PR_Send(sd, buffer, buflen, 0, timeout); michael@0: if (rv < 0) { michael@0: /* PR_Send() has invoked PR_SetError(). */ michael@0: rv = -1; michael@0: goto done; michael@0: } else { michael@0: count += rv; michael@0: buffer = (const void*) ((const char*)buffer + rv); michael@0: buflen -= rv; michael@0: } michael@0: } michael@0: rv = count; michael@0: michael@0: done: michael@0: if (buf) michael@0: PR_DELETE(buf); michael@0: if ((rv >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) michael@0: PR_Close(sd); michael@0: return rv; michael@0: } michael@0: michael@0: #endif michael@0: michael@0: /* priometh.c */