1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "primpl.h"
7
8 #include <string.h>
9
10 /*****************************************************************************/
11 /************************** Invalid I/O method object ************************/
12 /*****************************************************************************/
13 PRIOMethods _pr_faulty_methods = {
14 (PRDescType)0,
15 (PRCloseFN)_PR_InvalidStatus,
16 (PRReadFN)_PR_InvalidInt,
17 (PRWriteFN)_PR_InvalidInt,
18 (PRAvailableFN)_PR_InvalidInt,
19 (PRAvailable64FN)_PR_InvalidInt64,
20 (PRFsyncFN)_PR_InvalidStatus,
21 (PRSeekFN)_PR_InvalidInt,
22 (PRSeek64FN)_PR_InvalidInt64,
23 (PRFileInfoFN)_PR_InvalidStatus,
24 (PRFileInfo64FN)_PR_InvalidStatus,
25 (PRWritevFN)_PR_InvalidInt,
26 (PRConnectFN)_PR_InvalidStatus,
27 (PRAcceptFN)_PR_InvalidDesc,
28 (PRBindFN)_PR_InvalidStatus,
29 (PRListenFN)_PR_InvalidStatus,
30 (PRShutdownFN)_PR_InvalidStatus,
31 (PRRecvFN)_PR_InvalidInt,
32 (PRSendFN)_PR_InvalidInt,
33 (PRRecvfromFN)_PR_InvalidInt,
34 (PRSendtoFN)_PR_InvalidInt,
35 (PRPollFN)_PR_InvalidInt16,
36 (PRAcceptreadFN)_PR_InvalidInt,
37 (PRTransmitfileFN)_PR_InvalidInt,
38 (PRGetsocknameFN)_PR_InvalidStatus,
39 (PRGetpeernameFN)_PR_InvalidStatus,
40 (PRReservedFN)_PR_InvalidInt,
41 (PRReservedFN)_PR_InvalidInt,
42 (PRGetsocketoptionFN)_PR_InvalidStatus,
43 (PRSetsocketoptionFN)_PR_InvalidStatus,
44 (PRSendfileFN)_PR_InvalidInt,
45 (PRConnectcontinueFN)_PR_InvalidStatus,
46 (PRReservedFN)_PR_InvalidInt,
47 (PRReservedFN)_PR_InvalidInt,
48 (PRReservedFN)_PR_InvalidInt,
49 (PRReservedFN)_PR_InvalidInt
50 };
51
_PR_InvalidInt(void)52 PRIntn _PR_InvalidInt(void)
53 {
54 PR_NOT_REACHED("I/O method is invalid");
55 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
56 return -1;
57 } /* _PR_InvalidInt */
58
_PR_InvalidInt16(void)59 PRInt16 _PR_InvalidInt16(void)
60 {
61 PR_NOT_REACHED("I/O method is invalid");
62 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
63 return -1;
64 } /* _PR_InvalidInt */
65
_PR_InvalidInt64(void)66 PRInt64 _PR_InvalidInt64(void)
67 {
68 PRInt64 rv;
69 LL_I2L(rv, -1);
70 PR_NOT_REACHED("I/O method is invalid");
71 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
72 return rv;
73 } /* _PR_InvalidInt */
74
75 /*
76 * An invalid method that returns PRStatus
77 */
78
_PR_InvalidStatus(void)79 PRStatus _PR_InvalidStatus(void)
80 {
81 PR_NOT_REACHED("I/O method is invalid");
82 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
83 return PR_FAILURE;
84 } /* _PR_InvalidDesc */
85
86 /*
87 * An invalid method that returns a pointer
88 */
89
_PR_InvalidDesc(void)90 PRFileDesc *_PR_InvalidDesc(void)
91 {
92 PR_NOT_REACHED("I/O method is invalid");
93 PR_SetError(PR_INVALID_METHOD_ERROR, 0);
94 return NULL;
95 } /* _PR_InvalidDesc */
96
PR_GetDescType(PRFileDesc * file)97 PR_IMPLEMENT(PRDescType) PR_GetDescType(PRFileDesc *file)
98 {
99 return file->methods->file_type;
100 }
101
PR_Close(PRFileDesc * fd)102 PR_IMPLEMENT(PRStatus) PR_Close(PRFileDesc *fd)
103 {
104 return (fd->methods->close)(fd);
105 }
106
PR_Read(PRFileDesc * fd,void * buf,PRInt32 amount)107 PR_IMPLEMENT(PRInt32) PR_Read(PRFileDesc *fd, void *buf, PRInt32 amount)
108 {
109 return((fd->methods->read)(fd,buf,amount));
110 }
111
PR_Write(PRFileDesc * fd,const void * buf,PRInt32 amount)112 PR_IMPLEMENT(PRInt32) PR_Write(PRFileDesc *fd, const void *buf, PRInt32 amount)
113 {
114 return((fd->methods->write)(fd,buf,amount));
115 }
116
PR_Seek(PRFileDesc * fd,PRInt32 offset,PRSeekWhence whence)117 PR_IMPLEMENT(PRInt32) PR_Seek(PRFileDesc *fd, PRInt32 offset, PRSeekWhence whence)
118 {
119 return((fd->methods->seek)(fd, offset, whence));
120 }
121
PR_Seek64(PRFileDesc * fd,PRInt64 offset,PRSeekWhence whence)122 PR_IMPLEMENT(PRInt64) PR_Seek64(PRFileDesc *fd, PRInt64 offset, PRSeekWhence whence)
123 {
124 return((fd->methods->seek64)(fd, offset, whence));
125 }
126
PR_Available(PRFileDesc * fd)127 PR_IMPLEMENT(PRInt32) PR_Available(PRFileDesc *fd)
128 {
129 return((fd->methods->available)(fd));
130 }
131
PR_Available64(PRFileDesc * fd)132 PR_IMPLEMENT(PRInt64) PR_Available64(PRFileDesc *fd)
133 {
134 return((fd->methods->available64)(fd));
135 }
136
PR_GetOpenFileInfo(PRFileDesc * fd,PRFileInfo * info)137 PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo(PRFileDesc *fd, PRFileInfo *info)
138 {
139 return((fd->methods->fileInfo)(fd, info));
140 }
141
PR_GetOpenFileInfo64(PRFileDesc * fd,PRFileInfo64 * info)142 PR_IMPLEMENT(PRStatus) PR_GetOpenFileInfo64(PRFileDesc *fd, PRFileInfo64 *info)
143 {
144 return((fd->methods->fileInfo64)(fd, info));
145 }
146
PR_Sync(PRFileDesc * fd)147 PR_IMPLEMENT(PRStatus) PR_Sync(PRFileDesc *fd)
148 {
149 return((fd->methods->fsync)(fd));
150 }
151
PR_Connect(PRFileDesc * fd,const PRNetAddr * addr,PRIntervalTime timeout)152 PR_IMPLEMENT(PRStatus) PR_Connect(
153 PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout)
154 {
155 return((fd->methods->connect)(fd,addr,timeout));
156 }
157
PR_ConnectContinue(PRFileDesc * fd,PRInt16 out_flags)158 PR_IMPLEMENT(PRStatus) PR_ConnectContinue(
159 PRFileDesc *fd, PRInt16 out_flags)
160 {
161 return((fd->methods->connectcontinue)(fd,out_flags));
162 }
163
PR_Accept(PRFileDesc * fd,PRNetAddr * addr,PRIntervalTime timeout)164 PR_IMPLEMENT(PRFileDesc*) PR_Accept(PRFileDesc *fd, PRNetAddr *addr,
165 PRIntervalTime timeout)
166 {
167 return((fd->methods->accept)(fd,addr,timeout));
168 }
169
PR_Bind(PRFileDesc * fd,const PRNetAddr * addr)170 PR_IMPLEMENT(PRStatus) PR_Bind(PRFileDesc *fd, const PRNetAddr *addr)
171 {
172 return((fd->methods->bind)(fd,addr));
173 }
174
PR_Shutdown(PRFileDesc * fd,PRShutdownHow how)175 PR_IMPLEMENT(PRStatus) PR_Shutdown(PRFileDesc *fd, PRShutdownHow how)
176 {
177 return((fd->methods->shutdown)(fd,how));
178 }
179
PR_Listen(PRFileDesc * fd,PRIntn backlog)180 PR_IMPLEMENT(PRStatus) PR_Listen(PRFileDesc *fd, PRIntn backlog)
181 {
182 return((fd->methods->listen)(fd,backlog));
183 }
184
PR_Recv(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)185 PR_IMPLEMENT(PRInt32) PR_Recv(PRFileDesc *fd, void *buf, PRInt32 amount,
186 PRIntn flags, PRIntervalTime timeout)
187 {
188 return((fd->methods->recv)(fd,buf,amount,flags,timeout));
189 }
190
PR_Send(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,PRIntervalTime timeout)191 PR_IMPLEMENT(PRInt32) PR_Send(PRFileDesc *fd, const void *buf, PRInt32 amount,
192 PRIntn flags, PRIntervalTime timeout)
193 {
194 return((fd->methods->send)(fd,buf,amount,flags,timeout));
195 }
196
PR_Writev(PRFileDesc * fd,const PRIOVec * iov,PRInt32 iov_size,PRIntervalTime timeout)197 PR_IMPLEMENT(PRInt32) PR_Writev(PRFileDesc *fd, const PRIOVec *iov,
198 PRInt32 iov_size, PRIntervalTime timeout)
199 {
200 if (iov_size > PR_MAX_IOVECTOR_SIZE)
201 {
202 PR_SetError(PR_BUFFER_OVERFLOW_ERROR, 0);
203 return -1;
204 }
205 return((fd->methods->writev)(fd,iov,iov_size,timeout));
206 }
207
PR_RecvFrom(PRFileDesc * fd,void * buf,PRInt32 amount,PRIntn flags,PRNetAddr * addr,PRIntervalTime timeout)208 PR_IMPLEMENT(PRInt32) PR_RecvFrom(PRFileDesc *fd, void *buf, PRInt32 amount,
209 PRIntn flags, PRNetAddr *addr, PRIntervalTime timeout)
210 {
211 return((fd->methods->recvfrom)(fd,buf,amount,flags,addr,timeout));
212 }
213
PR_SendTo(PRFileDesc * fd,const void * buf,PRInt32 amount,PRIntn flags,const PRNetAddr * addr,PRIntervalTime timeout)214 PR_IMPLEMENT(PRInt32) PR_SendTo(
215 PRFileDesc *fd, const void *buf, PRInt32 amount,
216 PRIntn flags, const PRNetAddr *addr, PRIntervalTime timeout)
217 {
218 return((fd->methods->sendto)(fd,buf,amount,flags,addr,timeout));
219 }
220
PR_TransmitFile(PRFileDesc * sd,PRFileDesc * fd,const void * hdr,PRInt32 hlen,PRTransmitFileFlags flags,PRIntervalTime timeout)221 PR_IMPLEMENT(PRInt32) PR_TransmitFile(
222 PRFileDesc *sd, PRFileDesc *fd, const void *hdr, PRInt32 hlen,
223 PRTransmitFileFlags flags, PRIntervalTime timeout)
224 {
225 return((sd->methods->transmitfile)(sd,fd,hdr,hlen,flags,timeout));
226 }
227
PR_AcceptRead(PRFileDesc * sd,PRFileDesc ** nd,PRNetAddr ** raddr,void * buf,PRInt32 amount,PRIntervalTime timeout)228 PR_IMPLEMENT(PRInt32) PR_AcceptRead(
229 PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
230 void *buf, PRInt32 amount, PRIntervalTime timeout)
231 {
232 return((sd->methods->acceptread)(sd, nd, raddr, buf, amount,timeout));
233 }
234
PR_GetSockName(PRFileDesc * fd,PRNetAddr * addr)235 PR_IMPLEMENT(PRStatus) PR_GetSockName(PRFileDesc *fd, PRNetAddr *addr)
236 {
237 return((fd->methods->getsockname)(fd,addr));
238 }
239
PR_GetPeerName(PRFileDesc * fd,PRNetAddr * addr)240 PR_IMPLEMENT(PRStatus) PR_GetPeerName(PRFileDesc *fd, PRNetAddr *addr)
241 {
242 return((fd->methods->getpeername)(fd,addr));
243 }
244
PR_GetSocketOption(PRFileDesc * fd,PRSocketOptionData * data)245 PR_IMPLEMENT(PRStatus) PR_GetSocketOption(
246 PRFileDesc *fd, PRSocketOptionData *data)
247 {
248 return((fd->methods->getsocketoption)(fd, data));
249 }
250
PR_SetSocketOption(PRFileDesc * fd,const PRSocketOptionData * data)251 PR_IMPLEMENT(PRStatus) PR_SetSocketOption(
252 PRFileDesc *fd, const PRSocketOptionData *data)
253 {
254 return((fd->methods->setsocketoption)(fd, data));
255 }
256
PR_SendFile(PRFileDesc * sd,PRSendFileData * sfd,PRTransmitFileFlags flags,PRIntervalTime timeout)257 PR_IMPLEMENT(PRInt32) PR_SendFile(
258 PRFileDesc *sd, PRSendFileData *sfd,
259 PRTransmitFileFlags flags, PRIntervalTime timeout)
260 {
261 return((sd->methods->sendfile)(sd,sfd,flags,timeout));
262 }
263
PR_EmulateAcceptRead(PRFileDesc * sd,PRFileDesc ** nd,PRNetAddr ** raddr,void * buf,PRInt32 amount,PRIntervalTime timeout)264 PR_IMPLEMENT(PRInt32) PR_EmulateAcceptRead(
265 PRFileDesc *sd, PRFileDesc **nd, PRNetAddr **raddr,
266 void *buf, PRInt32 amount, PRIntervalTime timeout)
267 {
268 PRInt32 rv = -1;
269 PRNetAddr remote;
270 PRFileDesc *accepted = NULL;
271
272 /*
273 ** The timeout does not apply to the accept portion of the
274 ** operation - it waits indefinitely.
275 */
276 accepted = PR_Accept(sd, &remote, PR_INTERVAL_NO_TIMEOUT);
277 if (NULL == accepted) {
278 return rv;
279 }
280
281 rv = PR_Recv(accepted, buf, amount, 0, timeout);
282 if (rv >= 0)
283 {
284 /* copy the new info out where caller can see it */
285 #define AMASK ((PRPtrdiff)7) /* mask for alignment of PRNetAddr */
286 PRPtrdiff aligned = (PRPtrdiff)buf + amount + AMASK;
287 *raddr = (PRNetAddr*)(aligned & ~AMASK);
288 memcpy(*raddr, &remote, PR_NETADDR_SIZE(&remote));
289 *nd = accepted;
290 return rv;
291 }
292
293 PR_Close(accepted);
294 return rv;
295 }
296
297 /*
298 * PR_EmulateSendFile
299 *
300 * Send file sfd->fd across socket sd. If header/trailer are specified
301 * they are sent before and after the file, respectively.
302 *
303 * PR_TRANSMITFILE_CLOSE_SOCKET flag - close socket after sending file
304 *
305 * return number of bytes sent or -1 on error
306 *
307 */
308
309 #if defined(XP_UNIX) || defined(WIN32)
310
311 /*
312 * An implementation based on memory-mapped files
313 */
314
315 #define SENDFILE_MMAP_CHUNK (256 * 1024)
316
PR_EmulateSendFile(PRFileDesc * sd,PRSendFileData * sfd,PRTransmitFileFlags flags,PRIntervalTime timeout)317 PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
318 PRFileDesc *sd, PRSendFileData *sfd,
319 PRTransmitFileFlags flags, PRIntervalTime timeout)
320 {
321 PRInt32 rv, count = 0;
322 PRInt32 len, file_bytes, index = 0;
323 PRFileInfo info;
324 PRIOVec iov[3];
325 PRFileMap *mapHandle = NULL;
326 void *addr = (void*)0; /* initialized to some arbitrary value. Keeps compiler warnings down. */
327 PRUint32 file_mmap_offset, alignment;
328 PRInt64 zero64;
329 PROffset64 file_mmap_offset64;
330 PRUint32 addr_offset, mmap_len;
331
332 /* Get file size */
333 if (PR_SUCCESS != PR_GetOpenFileInfo(sfd->fd, &info)) {
334 count = -1;
335 goto done;
336 }
337 if (sfd->file_nbytes &&
338 (info.size < (sfd->file_offset + sfd->file_nbytes))) {
339 /*
340 * there are fewer bytes in file to send than specified
341 */
342 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
343 count = -1;
344 goto done;
345 }
346 if (sfd->file_nbytes) {
347 file_bytes = sfd->file_nbytes;
348 }
349 else {
350 file_bytes = info.size - sfd->file_offset;
351 }
352
353 alignment = PR_GetMemMapAlignment();
354
355 /* number of initial bytes to skip in mmap'd segment */
356 addr_offset = sfd->file_offset % alignment;
357
358 /* find previous mmap alignment boundary */
359 file_mmap_offset = sfd->file_offset - addr_offset;
360
361 /*
362 * If the file is large, mmap and send the file in chunks so as
363 * to not consume too much virtual address space
364 */
365 mmap_len = PR_MIN(file_bytes + addr_offset, SENDFILE_MMAP_CHUNK);
366 len = mmap_len - addr_offset;
367
368 /*
369 * Map in (part of) file. Take care of zero-length files.
370 */
371 if (len) {
372 LL_I2L(zero64, 0);
373 mapHandle = PR_CreateFileMap(sfd->fd, zero64, PR_PROT_READONLY);
374 if (!mapHandle) {
375 count = -1;
376 goto done;
377 }
378 LL_I2L(file_mmap_offset64, file_mmap_offset);
379 addr = PR_MemMap(mapHandle, file_mmap_offset64, mmap_len);
380 if (!addr) {
381 count = -1;
382 goto done;
383 }
384 }
385 /*
386 * send headers first, followed by the file
387 */
388 if (sfd->hlen) {
389 iov[index].iov_base = (char *) sfd->header;
390 iov[index].iov_len = sfd->hlen;
391 index++;
392 }
393 if (len) {
394 iov[index].iov_base = (char*)addr + addr_offset;
395 iov[index].iov_len = len;
396 index++;
397 }
398 if ((file_bytes == len) && (sfd->tlen)) {
399 /*
400 * all file data is mapped in; send the trailer too
401 */
402 iov[index].iov_base = (char *) sfd->trailer;
403 iov[index].iov_len = sfd->tlen;
404 index++;
405 }
406 rv = PR_Writev(sd, iov, index, timeout);
407 if (len) {
408 PR_MemUnmap(addr, mmap_len);
409 }
410 if (rv < 0) {
411 count = -1;
412 goto done;
413 }
414
415 PR_ASSERT(rv == sfd->hlen + len + ((len == file_bytes) ? sfd->tlen : 0));
416
417 file_bytes -= len;
418 count += rv;
419 if (!file_bytes) { /* header, file and trailer are sent */
420 goto done;
421 }
422
423 /*
424 * send remaining bytes of the file, if any
425 */
426 len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
427 while (len > 0) {
428 /*
429 * Map in (part of) file
430 */
431 file_mmap_offset = sfd->file_offset + count - sfd->hlen;
432 PR_ASSERT((file_mmap_offset % alignment) == 0);
433
434 LL_I2L(file_mmap_offset64, file_mmap_offset);
435 addr = PR_MemMap(mapHandle, file_mmap_offset64, len);
436 if (!addr) {
437 count = -1;
438 goto done;
439 }
440 rv = PR_Send(sd, addr, len, 0, timeout);
441 PR_MemUnmap(addr, len);
442 if (rv < 0) {
443 count = -1;
444 goto done;
445 }
446
447 PR_ASSERT(rv == len);
448 file_bytes -= rv;
449 count += rv;
450 len = PR_MIN(file_bytes, SENDFILE_MMAP_CHUNK);
451 }
452 PR_ASSERT(0 == file_bytes);
453 if (sfd->tlen) {
454 rv = PR_Send(sd, sfd->trailer, sfd->tlen, 0, timeout);
455 if (rv >= 0) {
456 PR_ASSERT(rv == sfd->tlen);
457 count += rv;
458 } else {
459 count = -1;
460 }
461 }
462 done:
463 if (mapHandle) {
464 PR_CloseFileMap(mapHandle);
465 }
466 if ((count >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
467 PR_Close(sd);
468 }
469 return count;
470 }
471
472 #else
473
PR_EmulateSendFile(PRFileDesc * sd,PRSendFileData * sfd,PRTransmitFileFlags flags,PRIntervalTime timeout)474 PR_IMPLEMENT(PRInt32) PR_EmulateSendFile(
475 PRFileDesc *sd, PRSendFileData *sfd,
476 PRTransmitFileFlags flags, PRIntervalTime timeout)
477 {
478 PRInt32 rv, count = 0;
479 PRInt32 rlen;
480 const void * buffer;
481 PRInt32 buflen;
482 PRInt32 sendbytes, readbytes;
483 char *buf;
484
485 #define _SENDFILE_BUFSIZE (16 * 1024)
486
487 buf = (char*)PR_MALLOC(_SENDFILE_BUFSIZE);
488 if (buf == NULL) {
489 PR_SetError(PR_OUT_OF_MEMORY_ERROR, 0);
490 return -1;
491 }
492
493 /*
494 * send header first
495 */
496 buflen = sfd->hlen;
497 buffer = sfd->header;
498 while (buflen) {
499 rv = PR_Send(sd, buffer, buflen, 0, timeout);
500 if (rv < 0) {
501 /* PR_Send() has invoked PR_SetError(). */
502 rv = -1;
503 goto done;
504 } else {
505 count += rv;
506 buffer = (const void*) ((const char*)buffer + rv);
507 buflen -= rv;
508 }
509 }
510
511 /*
512 * send file next
513 */
514 if (PR_Seek(sfd->fd, sfd->file_offset, PR_SEEK_SET) < 0) {
515 rv = -1;
516 goto done;
517 }
518 sendbytes = sfd->file_nbytes;
519 if (sendbytes == 0) {
520 /* send entire file */
521 while ((rlen = PR_Read(sfd->fd, buf, _SENDFILE_BUFSIZE)) > 0) {
522 while (rlen) {
523 char *bufptr = buf;
524
525 rv = PR_Send(sd, bufptr, rlen, 0, timeout);
526 if (rv < 0) {
527 /* PR_Send() has invoked PR_SetError(). */
528 rv = -1;
529 goto done;
530 } else {
531 count += rv;
532 bufptr = ((char*)bufptr + rv);
533 rlen -= rv;
534 }
535 }
536 }
537 if (rlen < 0) {
538 /* PR_Read() has invoked PR_SetError(). */
539 rv = -1;
540 goto done;
541 }
542 } else {
543 readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
544 while (readbytes && ((rlen = PR_Read(sfd->fd, buf, readbytes)) > 0)) {
545 while (rlen) {
546 char *bufptr = buf;
547
548 rv = PR_Send(sd, bufptr, rlen, 0, timeout);
549 if (rv < 0) {
550 /* PR_Send() has invoked PR_SetError(). */
551 rv = -1;
552 goto done;
553 } else {
554 count += rv;
555 sendbytes -= rv;
556 bufptr = ((char*)bufptr + rv);
557 rlen -= rv;
558 }
559 }
560 readbytes = PR_MIN(sendbytes, _SENDFILE_BUFSIZE);
561 }
562 if (rlen < 0) {
563 /* PR_Read() has invoked PR_SetError(). */
564 rv = -1;
565 goto done;
566 } else if (sendbytes != 0) {
567 /*
568 * there are fewer bytes in file to send than specified
569 */
570 PR_SetError(PR_INVALID_ARGUMENT_ERROR, 0);
571 rv = -1;
572 goto done;
573 }
574 }
575
576 /*
577 * send trailer last
578 */
579 buflen = sfd->tlen;
580 buffer = sfd->trailer;
581 while (buflen) {
582 rv = PR_Send(sd, buffer, buflen, 0, timeout);
583 if (rv < 0) {
584 /* PR_Send() has invoked PR_SetError(). */
585 rv = -1;
586 goto done;
587 } else {
588 count += rv;
589 buffer = (const void*) ((const char*)buffer + rv);
590 buflen -= rv;
591 }
592 }
593 rv = count;
594
595 done:
596 if (buf) {
597 PR_DELETE(buf);
598 }
599 if ((rv >= 0) && (flags & PR_TRANSMITFILE_CLOSE_SOCKET)) {
600 PR_Close(sd);
601 }
602 return rv;
603 }
604
605 #endif
606
607 /* priometh.c */
608