3 * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
10 static int gGotBrokenData
= 0;
12 #if defined(WIN32) || defined(_WINDOWS)
13 # define ASCII_TRANSLATION 0
16 #ifndef ASCII_TRANSLATION
17 # define ASCII_TRANSLATION 1
20 #if !defined(NO_SIGNALS) && (USE_SIO || !defined(SIGALRM) || !defined(SIGPIPE) || !defined(SIGINT))
27 static sigjmp_buf gBrokenDataJmp
;
29 static jmp_buf gBrokenDataJmp
;
30 #endif /* HAVE_SIGSETJMP */
31 static int gCanBrokenDataJmp
= 0;
33 #endif /* NO_SIGNALS */
37 /* Needed for platforms using different EOLN sequence (i.e. DOS) */
39 # define O_BINARY _O_BINARY
45 static int WaitForRemoteInput(const FTPCIPtr cip
);
46 static int WaitForRemoteOutput(const FTPCIPtr cip
);
52 BrokenData(int signum
)
54 gGotBrokenData
= signum
;
55 if (gCanBrokenDataJmp
!= 0) {
56 gCanBrokenDataJmp
= 0;
58 siglongjmp(gBrokenDataJmp
, 1);
60 longjmp(gBrokenDataJmp
, 1);
61 #endif /* HAVE_SIGSETJMP */
65 #endif /* NO_SIGNALS */
71 FTPInitIOTimer(const FTPCIPtr cip
)
73 cip
->bytesTransferred
= (longest_int
) 0;
74 cip
->expectedSize
= kSizeUnknown
;
75 cip
->mdtm
= kModTimeUnknown
;
78 cip
->kBytesPerSec
= -1.0;
79 cip
->percentCompleted
= -1.0;
82 cip
->nextProgressUpdate
= 0;
84 cip
->dataTimedOut
= 0;
85 cip
->useProgressMeter
= 1;
86 (void) gettimeofday(&cip
->t0
, NULL
);
87 } /* FTPInitIOTimer */
93 FTPStartIOTimer(const FTPCIPtr cip
)
95 (void) gettimeofday(&cip
->t0
, NULL
);
96 if (cip
->progress
!= (FTPProgressMeterProc
) 0)
97 (*cip
->progress
)(cip
, kPrInitMsg
);
98 } /* FTPStartIOTimer */
104 FTPUpdateIOTimer(const FTPCIPtr cip
)
107 struct timeval
*t0
, t1
;
111 if (now
< cip
->nextProgressUpdate
)
114 cip
->nextProgressUpdate
= now
;
116 (void) gettimeofday(&t1
, NULL
);
119 if (t0
->tv_usec
> t1
.tv_usec
) {
120 t1
.tv_usec
+= 1000000;
123 sec
= ((double) (t1
.tv_usec
- t0
->tv_usec
) * 0.000001)
124 + (t1
.tv_sec
- t0
->tv_sec
);
126 cip
->kBytesPerSec
= ((double) cip
->bytesTransferred
) / (1024.0 * sec
);
128 cip
->kBytesPerSec
= -1.0;
130 if (cip
->expectedSize
== kSizeUnknown
) {
131 cip
->percentCompleted
= -1.0;
133 } else if (cip
->expectedSize
<= 0) {
134 cip
->percentCompleted
= 100.0;
137 cip
->percentCompleted
= ((double) (100.0 * (cip
->bytesTransferred
+ cip
->startPoint
))) / ((double) cip
->expectedSize
);
138 if (cip
->percentCompleted
>= 100.0) {
139 cip
->percentCompleted
= 100.0;
141 } else if (cip
->percentCompleted
<= 0.0) {
142 cip
->secLeft
= 999.0;
144 if (cip
->kBytesPerSec
> 0.0) {
145 cip
->secLeft
= ((cip
->expectedSize
- cip
->bytesTransferred
- cip
->startPoint
) / 1024.0) / cip
->kBytesPerSec
;
146 if (cip
->secLeft
< 0.0)
151 if ((cip
->progress
!= (FTPProgressMeterProc
) 0) && (cip
->useProgressMeter
!= 0))
152 (*cip
->progress
)(cip
, kPrUpdateMsg
);
153 } /* FTPUpdateIOTimer */
159 FTPStopIOTimer(const FTPCIPtr cip
)
161 cip
->nextProgressUpdate
= 0; /* force last update */
162 FTPUpdateIOTimer(cip
);
163 if (cip
->progress
!= (FTPProgressMeterProc
) 0)
164 (*cip
->progress
)(cip
, kPrEndMsg
);
165 } /* FTPStopIOTimer */
170 /* This isn't too useful -- it mostly serves as an example so you can write
171 * your own function to do what you need to do with the listing.
174 FTPList(const FTPCIPtr cip
, const int outfd
, const int longMode
, const char *const lsflag
)
178 char secondaryBuf
[768];
180 char *secBufPtr
, *secBufLimit
;
183 #else /* NO_SIGNALS */
186 #endif /* NO_SIGNALS */
189 return (kErrBadParameter
);
190 if (strcmp(cip
->magic
, kLibraryMagic
))
191 return (kErrBadMagic
);
193 cmd
= (longMode
!= 0) ? "LIST" : "NLST";
194 if ((lsflag
== NULL
) || (lsflag
[0] == '\0')) {
195 result
= FTPStartDataCmd(cip
, kNetReading
, kTypeAscii
, (longest_int
) 0, "%s", cmd
);
197 result
= FTPStartDataCmd(cip
, kNetReading
, kTypeAscii
, (longest_int
) 0, "%s %s", cmd
, lsflag
);
204 if (InitSReadlineInfo(&lsSrl
, cip
->dataSocket
, secondaryBuf
, sizeof(secondaryBuf
), (int) cip
->xferTimeout
, 1) < 0) {
205 /* Not really fdopen, but close in what we're trying to do. */
206 result
= kErrFdopenR
;
207 cip
->errNo
= kErrFdopenR
;
208 Error(cip
, kDoPerror
, "Could not fdopen.\n");
213 result
= SReadline(&lsSrl
, line
, sizeof(line
) - 2);
214 if (result
== kTimeoutErr
) {
216 Error(cip
, kDontPerror
, "Could not directory listing data -- timed out.\n");
217 cip
->errNo
= kErrDataTimedOut
;
219 } else if (result
== 0) {
220 /* end of listing -- done */
223 } else if (result
< 0) {
225 Error(cip
, kDoPerror
, "Could not read directory listing data");
226 result
= kErrLISTFailed
;
227 cip
->errNo
= kErrLISTFailed
;
231 (void) write(outfd
, line
, strlen(line
));
234 DisposeSReadlineInfo(&lsSrl
);
235 if (FTPEndDataCmd(cip
, 1) < 0) {
236 result
= kErrLISTFailed
;
237 cip
->errNo
= kErrLISTFailed
;
239 } else if (result
== kErrGeneric
) {
240 result
= kErrLISTFailed
;
241 cip
->errNo
= kErrLISTFailed
;
245 #else /* NO_SIGNALS */
248 /* This line sets the buffer pointer so that the first thing
249 * BufferGets will do is reset and fill the buffer using
252 secBufPtr
= secondaryBuf
+ sizeof(secondaryBuf
);
253 secBufLimit
= (char *) 0;
256 if (cip
->xferTimeout
> 0)
257 (void) alarm(cip
->xferTimeout
);
258 nread
= BufferGets(line
, sizeof(line
), cip
->dataSocket
, secondaryBuf
, &secBufPtr
, &secBufLimit
, sizeof(secondaryBuf
));
263 cip
->bytesTransferred
+= (longest_int
) nread
;
264 (void) STRNCAT(line
, "\n");
265 (void) write(outfd
, line
, strlen(line
));
268 if (cip
->xferTimeout
> 0)
270 result
= FTPEndDataCmd(cip
, 1);
272 result
= kErrLISTFailed
;
273 cip
->errNo
= kErrLISTFailed
;
277 } else if (result
== kErrGeneric
) {
278 result
= kErrLISTFailed
;
279 cip
->errNo
= kErrLISTFailed
;
281 #endif /* NO_SIGNALS */
289 FTPRequestMlsOptions(const FTPCIPtr cip
)
295 if (cip
->usedMLS
== 0) {
296 /* First MLSD/MLST ? */
299 f
= cip
->mlsFeatures
& kPreferredMlsOpts
;
303 if ((f
& kMlsOptType
) != 0) {
304 STRNCAT(optstr
, "type;");
308 if ((f
& kMlsOptSize
) != 0) {
309 STRNCAT(optstr
, "size;");
313 if ((f
& kMlsOptModify
) != 0) {
314 STRNCAT(optstr
, "modify;");
318 if ((f
& kMlsOptUNIXmode
) != 0) {
319 STRNCAT(optstr
, "UNIX.mode;");
323 if ((f
& kMlsOptPerm
) != 0) {
324 STRNCAT(optstr
, "perm;");
328 if ((f
& kMlsOptUNIXowner
) != 0) {
329 STRNCAT(optstr
, "UNIX.owner;");
333 if ((f
& kMlsOptUNIXuid
) != 0) {
334 STRNCAT(optstr
, "UNIX.uid;");
338 if ((f
& kMlsOptUNIXgroup
) != 0) {
339 STRNCAT(optstr
, "UNIX.group;");
343 if ((f
& kMlsOptUNIXgid
) != 0) {
344 STRNCAT(optstr
, "UNIX.gid;");
348 if ((f
& kMlsOptUnique
) != 0) {
349 STRNCAT(optstr
, "unique;");
352 /* Tell the server what we prefer. */
353 optstrlen
= strlen(optstr
);
355 if (optstr
[optstrlen
- 1] == ';')
356 optstr
[optstrlen
- 1] = '\0';
357 (void) FTPCmd(cip
, "OPTS MLST %s", optstr
);
360 } /* FTPRequestMlsOptions */
366 FTPListToMemory2(const FTPCIPtr cip
, const char *const pattern
, const LineListPtr llines
, const char *const lsflags
, const int blankLines
, int *const tryMLSD
)
368 char secondaryBuf
[768];
371 const char *command
= "NLST";
375 char *secBufPtr
, *secBufLimit
;
376 volatile FTPSigProc osigpipe
;
377 volatile FTPCIPtr vcip
;
381 #else /* NO_SIGNALS */
384 #endif /* NO_SIGNALS */
387 return (kErrBadParameter
);
388 if (strcmp(cip
->magic
, kLibraryMagic
))
389 return (kErrBadMagic
);
391 if ((llines
== NULL
) || (pattern
== NULL
) || (lsflags
== NULL
))
392 return (kErrBadParameter
);
394 if ((tryMLSD
!= (int *) 0) && (*tryMLSD
!= 0) && (cip
->hasMLSD
== kCommandAvailable
)) {
396 if ((lsflags
[0] == '-') && (strchr(lsflags
, 'd') != NULL
) && (cip
->hasMLST
== kCommandAvailable
))
399 FTPRequestMlsOptions(cip
);
401 /* Not using MLSD. */
402 if (tryMLSD
!= (int *) 0)
404 if (lsflags
[0] == '-') {
405 /* See if we should use LIST instead. */
408 lim
= lsflags1
+ sizeof(lsflags1
) - 2;
409 for (; *scp
!= '\0'; scp
++) {
411 /* do not add the 'l' */
413 } else if (dcp
< lim
) {
421 (void) STRNCPY(lsflags1
, lsflags
);
425 InitLineList(llines
);
427 result
= FTPStartDataCmd(
434 (lsflags1
[0] == '\0') ? "" : " ",
436 (pattern
[0] == '\0') ? "" : " ",
443 if (InitSReadlineInfo(&lsSrl
, cip
->dataSocket
, secondaryBuf
, sizeof(secondaryBuf
), (int) cip
->xferTimeout
, 1) < 0) {
444 /* Not really fdopen, but close in what we're trying to do. */
445 result
= kErrFdopenR
;
446 cip
->errNo
= kErrFdopenR
;
447 Error(cip
, kDoPerror
, "Could not fdopen.\n");
452 result
= SReadline(&lsSrl
, line
, sizeof(line
) - 1);
453 if (result
== kTimeoutErr
) {
455 Error(cip
, kDontPerror
, "Could not directory listing data -- timed out.\n");
456 cip
->errNo
= kErrDataTimedOut
;
458 } else if (result
== 0) {
459 /* end of listing -- done */
462 } else if (result
< 0) {
464 Error(cip
, kDoPerror
, "Could not read directory listing data");
465 result
= kErrLISTFailed
;
466 cip
->errNo
= kErrLISTFailed
;
470 if (line
[result
- 1] == '\n')
471 line
[result
- 1] = '\0';
473 if ((blankLines
== 0) && (result
<= 1))
476 /* Valid directory listing line of output */
477 if ((line
[0] == '.') && ((line
[1] == '\0') || ((line
[1] == '.') && ((line
[2] == '\0') || (iscntrl(line
[2]))))))
478 continue; /* Skip . and .. */
480 (void) AddLine(llines
, line
);
483 DisposeSReadlineInfo(&lsSrl
);
484 if (FTPEndDataCmd(cip
, 1) < 0) {
485 result
= kErrLISTFailed
;
486 cip
->errNo
= kErrLISTFailed
;
488 } else if (result
== kErrGeneric
) {
489 result
= kErrLISTFailed
;
490 cip
->errNo
= kErrLISTFailed
;
494 #else /* NO_SIGNALS */
496 osigpipe
= (volatile FTPSigProc
) signal(SIGPIPE
, BrokenData
);
499 gCanBrokenDataJmp
= 0;
501 #ifdef HAVE_SIGSETJMP
502 sj
= sigsetjmp(gBrokenDataJmp
, 1);
504 sj
= setjmp(gBrokenDataJmp
);
505 #endif /* HAVE_SIGSETJMP */
508 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
509 FTPShutdownHost(vcip
);
510 vcip
->errNo
= kErrRemoteHostClosedConnection
;
513 gCanBrokenDataJmp
= 1;
516 /* This line sets the buffer pointer so that the first thing
517 * BufferGets will do is reset and fill the buffer using
520 secBufPtr
= secondaryBuf
+ sizeof(secondaryBuf
);
521 secBufLimit
= (char *) 0;
522 memset(secondaryBuf
, 0, sizeof(secondaryBuf
));
525 memset(line
, 0, sizeof(line
));
526 if (cip
->xferTimeout
> 0)
527 (void) alarm(cip
->xferTimeout
);
528 nread
= BufferGets(line
, sizeof(line
), cip
->dataSocket
, secondaryBuf
, &secBufPtr
, &secBufLimit
, sizeof(secondaryBuf
));
533 (void) AddLine(llines
, line
);
535 cip
->bytesTransferred
+= (longest_int
) nread
;
537 if ((line
[0] == '.') && ((line
[1] == '\0') || ((line
[1] == '.') && ((line
[2] == '\0') || (iscntrl(line
[2]))))))
538 continue; /* Skip . and .. */
540 (void) AddLine(llines
, line
);
543 if (cip
->xferTimeout
> 0)
545 result
= FTPEndDataCmd(cip
, 1);
547 result
= kErrLISTFailed
;
548 cip
->errNo
= kErrLISTFailed
;
552 } else if (result
== kErrGeneric
) {
553 result
= kErrLISTFailed
;
554 cip
->errNo
= kErrLISTFailed
;
556 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
557 #endif /* NO_SIGNALS */
559 } /* FTPListToMemory2 */
565 AutomaticallyUseASCIIModeDependingOnExtension(const FTPCIPtr cip
, const char *const pathName
, int *const xtype
)
567 if ((*xtype
== kTypeBinary
) && (cip
->asciiFilenameExtensions
!= NULL
)) {
568 if (FilenameExtensionIndicatesASCII(pathName
, cip
->asciiFilenameExtensions
)) {
569 /* Matched -- send this file in ASCII mode
570 * instead of binary since it's extension
571 * appears to be that of a text file.
576 } /* AutomaticallyUseASCIIModeDependingOnExtension */
581 /* The purpose of this is to provide updates for the progress meters
582 * during lags. Return zero if the operation timed-out.
585 WaitForRemoteOutput(const FTPCIPtr cip
)
595 xferTimeout
= cip
->xferTimeout
;
599 fd
= cip
->dataSocket
;
603 ocancelXfer
= cip
->cancelXfer
;
607 while ((xferTimeout
<= 0) || (wsecs
< xferTimeout
)) {
608 if ((cip
->cancelXfer
!= 0) && (ocancelXfer
== 0)) {
609 /* leave cip->stalled -- could have been stalled and then canceled. */
617 result
= select(fd
+ 1, NULL
, SELECT_TYPE_ARG234
&ss
, SELECT_TYPE_ARG234
&ss2
, &tv
);
622 } else if (result
< 0) {
623 if (errno
!= EINTR
) {
630 cip
->stalled
= wsecs
;
632 FTPUpdateIOTimer(cip
);
635 #if !defined(NO_SIGNALS)
636 /* Shouldn't get here -- alarm() should have
639 (void) kill(getpid(), SIGALRM
);
640 #endif /* NO_SIGNALS */
642 cip
->dataTimedOut
= 1;
643 return (0); /* timed-out */
644 } /* WaitForRemoteOutput */
652 const char *const file
,
653 const char *volatile dstfile
,
656 const int appendflag
,
657 const char *volatile tmppfx
,
658 const char *volatile tmpsfx
,
659 const int resumeflag
,
660 const int deleteflag
,
661 const ConfirmResumeUploadProc resumeProc
)
665 const char *odstfile
;
668 int tmpResult
, result
;
672 #if ASCII_TRANSLATION
673 char *src
, *srclim
, *dst
;
678 longest_int startPoint
= 0;
681 #if !defined(NO_SIGNALS)
683 volatile FTPSigProc osigpipe
;
684 volatile FTPCIPtr vcip
;
685 volatile int vfd
, vfdtouse
;
686 #endif /* NO_SIGNALS */
687 volatile int vzaction
;
688 int zaction
= kConfirmResumeProcSaidBestGuess
;
690 if (cip
->buf
== NULL
) {
691 Error(cip
, kDoPerror
, "Transfer buffer not allocated.\n");
692 cip
->errNo
= kErrNoBuf
;
698 fd
= Open(file
, O_RDONLY
|O_BINARY
, 0);
700 Error(cip
, kDoPerror
, "Cannot open local file %s for reading.\n", file
);
701 cip
->errNo
= kErrOpenFailed
;
708 fstatrc
= Fstat(fd
, &st
);
709 if ((fstatrc
== 0) && (S_ISDIR(st
.st_mode
))) {
713 Error(cip
, kDontPerror
, "%s is a directory.\n", (file
!= NULL
) ? file
: "that");
714 cip
->errNo
= kErrOpenFailed
;
718 /* For Put, we can't recover very well if it turns out restart
719 * didn't work, so check beforehand.
721 if (cip
->hasREST
== kCommandAvailabilityUnknown
) {
722 (void) FTPSetTransferType(cip
, kTypeBinary
);
723 if (SetStartOffset(cip
, (longest_int
) 1) == kNoErr
) {
724 /* Now revert -- we still may not end up
727 SetStartOffset(cip
, (longest_int
) -1);
732 AutomaticallyUseASCIIModeDependingOnExtension(cip
, dstfile
, &xtype
);
733 (void) FTPFileSizeAndModificationTime(cip
, dstfile
, &startPoint
, xtype
, &mdtm
);
735 if (appendflag
== kAppendYes
) {
736 zaction
= kConfirmResumeProcSaidAppend
;
738 (cip
->hasREST
== kCommandNotAvailable
) ||
739 (xtype
!= kTypeBinary
) ||
742 zaction
= kConfirmResumeProcSaidOverwrite
;
743 } else if (resumeflag
== kResumeYes
) {
744 zaction
= kConfirmResumeProcSaidBestGuess
;
746 zaction
= kConfirmResumeProcSaidOverwrite
;
750 if ((mdtm
!= kModTimeUnknown
) || (startPoint
!= kSizeUnknown
)) {
751 /* Then we know the file exists. We will
752 * ask the user what to do, if possible, below.
755 } else if ((resumeProc
!= NoConfirmResumeUploadProc
) && (cip
->hasMDTM
!= kCommandAvailable
) && (cip
->hasSIZE
!= kCommandAvailable
)) {
756 /* We already checked if the file had a filesize
757 * or timestamp above, but if the server indicated
758 * it did not support querying those directly,
759 * we now need to try to determine if the file
760 * exists in a few other ways.
762 statrc
= FTPFileExists2(cip
, dstfile
, 0, 0, 0, 1, 1);
766 (resumeProc
!= NoConfirmResumeUploadProc
) &&
769 zaction
= (*resumeProc
)(file
, (longest_int
) st
.st_size
, st
.st_mtime
, &dstfile
, startPoint
, mdtm
, &startPoint
);
772 if (zaction
== kConfirmResumeProcSaidCancel
) {
773 /* User wants to cancel this file and any
774 * remaining in batch.
776 cip
->errNo
= kErrUserCanceled
;
780 if (zaction
== kConfirmResumeProcSaidBestGuess
) {
781 if ((mdtm
!= kModTimeUnknown
) && (st
.st_mtime
> (mdtm
+ 1))) {
782 /* Local file is newer than remote,
783 * overwrite the remote file instead
784 * of trying to resume it.
786 * Note: Add one second fudge factor
787 * for Windows' file timestamps being
788 * imprecise to one second.
790 zaction
= kConfirmResumeProcSaidOverwrite
;
791 } else if ((longest_int
) st
.st_size
== startPoint
) {
792 /* Already sent file, done. */
793 zaction
= kConfirmResumeProcSaidSkip
;
794 } else if ((startPoint
!= kSizeUnknown
) && ((longest_int
) st
.st_size
> startPoint
)) {
795 zaction
= kConfirmResumeProcSaidResume
;
797 zaction
= kConfirmResumeProcSaidOverwrite
;
801 if (zaction
== kConfirmResumeProcSaidSkip
) {
802 /* Nothing done, but not an error. */
806 if (deleteflag
== kDeleteYes
) {
807 if (unlink(file
) < 0) {
808 cip
->errNo
= kErrLocalDeleteFailed
;
813 } else if (zaction
== kConfirmResumeProcSaidResume
) {
814 /* Resume; proc set the startPoint. */
815 if ((longest_int
) st
.st_size
== startPoint
) {
816 /* Already sent file, done. */
821 if (deleteflag
== kDeleteYes
) {
822 if (unlink(file
) < 0) {
823 cip
->errNo
= kErrLocalDeleteFailed
;
828 } else if (Lseek(fd
, (off_t
) startPoint
, SEEK_SET
) != (off_t
) -1) {
829 cip
->startPoint
= startPoint
;
831 } else if (zaction
== kConfirmResumeProcSaidAppend
) {
832 /* append: leave startPoint at zero, we will append everything. */
833 cip
->startPoint
= startPoint
= 0;
834 } else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
835 /* overwrite: leave startPoint at zero */
836 cip
->startPoint
= startPoint
= 0;
840 if ((cip
->numUploads
== 0) && (cip
->dataSocketSBufSize
> 0)) {
841 /* If dataSocketSBufSize is non-zero, it means you
842 * want to explicitly try to set the size of the
843 * socket's I/O buffer.
845 * If it is zero, it means you want to just use the
846 * TCP stack's default value, which is typically
847 * between 8 and 64 kB.
849 * If you try to set the buffer larger than 64 kB,
850 * the TCP stack should try to use RFC 1323 to
851 * negotiate "TCP Large Windows" which may yield
852 * significant performance gains.
854 if (cip
->hasSTORBUFSIZE
== kCommandAvailable
)
855 (void) FTPCmd(cip
, "SITE STORBUFSIZE %lu", (unsigned long) cip
->dataSocketSBufSize
);
856 else if (cip
->hasSBUFSIZ
== kCommandAvailable
)
857 (void) FTPCmd(cip
, "SITE SBUFSIZ %lu", (unsigned long) cip
->dataSocketSBufSize
);
858 else if (cip
->hasSBUFSZ
== kCommandAvailable
)
859 (void) FTPCmd(cip
, "SITE SBUFSZ %lu", (unsigned long) cip
->dataSocketSBufSize
);
860 /* At least one server implemenation has RBUFSZ but not
861 * SBUFSZ and instead uses RBUFSZ for both.
863 else if ((cip
->hasSBUFSZ
!= kCommandAvailable
) && (cip
->hasRBUFSZ
== kCommandAvailable
))
864 (void) FTPCmd(cip
, "SITE RBUFSZ %lu", (unsigned long) cip
->dataSocketSBufSize
);
865 else if (cip
->hasBUFSIZE
== kCommandAvailable
)
866 (void) FTPCmd(cip
, "SITE BUFSIZE %lu", (unsigned long) cip
->dataSocketSBufSize
);
871 #else /* NO_SIGNALS */
876 osigpipe
= (volatile FTPSigProc
) signal(SIGPIPE
, BrokenData
);
879 gCanBrokenDataJmp
= 0;
881 #ifdef HAVE_SIGSETJMP
882 sj
= sigsetjmp(gBrokenDataJmp
, 1);
884 sj
= setjmp(gBrokenDataJmp
);
885 #endif /* HAVE_SIGSETJMP */
888 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
892 FTPShutdownHost(vcip
);
893 vcip
->errNo
= kErrRemoteHostClosedConnection
;
896 gCanBrokenDataJmp
= 1;
897 #endif /* NO_SIGNALS */
899 if (vzaction
== kConfirmResumeProcSaidAppend
) {
901 tmppfx
= ""; /* Can't use that here. */
912 if ((tmppfx
[0] != '\0') || (tmpsfx
[0] != '\0')) {
913 cp
= strrchr(dstfile
, '/');
915 cp
= strrchr(dstfile
, '\\');
917 (void) STRNCPY(dstfile2
, tmppfx
);
918 (void) STRNCAT(dstfile2
, dstfile
);
919 (void) STRNCAT(dstfile2
, tmpsfx
);
922 l
= (size_t) (cp
- dstfile
);
923 (void) STRNCPY(dstfile2
, dstfile
);
924 dstfile2
[l
] = '\0'; /* Nuke stuff after / */
925 (void) STRNCAT(dstfile2
, tmppfx
);
926 (void) STRNCAT(dstfile2
, cp
);
927 (void) STRNCAT(dstfile2
, tmpsfx
);
932 tmpResult
= FTPStartDataCmd(
943 cip
->errNo
= tmpResult
;
947 #if !defined(NO_SIGNALS)
948 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
949 #endif /* NO_SIGNALS */
953 if ((startPoint
!= 0) && (cip
->startPoint
== 0)) {
954 /* Remote could not or would not set the start offset
957 * So now we have to undo our seek.
959 if (Lseek(fd
, (off_t
) 0, SEEK_SET
) != (off_t
) 0) {
960 cip
->errNo
= kErrLseekFailed
;
964 #if !defined(NO_SIGNALS)
965 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
966 #endif /* NO_SIGNALS */
974 bufSize
= cip
->bufSize
;
977 if ((fstatrc
== 0) && (S_ISREG(st
.st_mode
) != 0)) {
978 cip
->expectedSize
= (longest_int
) st
.st_size
;
979 cip
->mdtm
= st
.st_mtime
;
981 cip
->lname
= file
; /* could be NULL */
982 cip
->rname
= odstfile
;
984 cip
->useProgressMeter
= 0;
985 FTPStartIOTimer(cip
);
987 /* Note: On Windows, we don't have to do anything special
988 * for ASCII mode, since Net ASCII's end-of-line sequence
989 * corresponds to the same thing used for DOS/Windows.
992 #if ASCII_TRANSLATION
993 if (xtype
== kTypeAscii
) {
996 #if !defined(NO_SIGNALS)
997 gCanBrokenDataJmp
= 0;
998 #endif /* NO_SIGNALS */
999 nread
= read(fd
, inbuf
, sizeof(inbuf
));
1001 if (errno
== EINTR
) {
1004 result
= kErrReadFailed
;
1005 cip
->errNo
= kErrReadFailed
;
1006 Error(cip
, kDoPerror
, "Local read failed.\n");
1009 } else if (nread
== 0) {
1012 cip
->bytesTransferred
+= (longest_int
) nread
;
1014 #if !defined(NO_SIGNALS)
1015 gCanBrokenDataJmp
= 1;
1016 #endif /* NO_SIGNALS */
1018 srclim
= src
+ nread
;
1019 dst
= cip
->buf
; /* must be 2x sizeof inbuf or more. */
1020 while (src
< srclim
) {
1025 ntowrite
= (size_t) (dst
- cip
->buf
);
1028 #if !defined(NO_SIGNALS)
1029 if (cip
->xferTimeout
> 0)
1030 (void) alarm(cip
->xferTimeout
);
1031 #endif /* NO_SIGNALS */
1033 if (! WaitForRemoteOutput(cip
)) { /* could set cancelXfer */
1034 cip
->errNo
= result
= kErrDataTimedOut
;
1035 Error(cip
, kDontPerror
, "Remote write timed out.\n");
1038 if (cip
->cancelXfer
> 0) {
1039 FTPAbortDataTransfer(cip
);
1040 result
= cip
->errNo
= kErrDataTransferAborted
;
1045 nwrote
= SWrite(cip
->dataSocket
, cp
, (size_t) ntowrite
, (int) cip
->xferTimeout
, kNoFirstSelect
);
1047 if (nwrote
== kTimeoutErr
) {
1048 cip
->errNo
= result
= kErrDataTimedOut
;
1049 Error(cip
, kDontPerror
, "Remote write timed out.\n");
1050 } else if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
1051 cip
->errNo
= result
= kErrSocketWriteFailed
;
1053 Error(cip
, kDoPerror
, "Lost data connection to remote host.\n");
1054 } else if (errno
== EINTR
) {
1057 cip
->errNo
= result
= kErrSocketWriteFailed
;
1058 Error(cip
, kDoPerror
, "Remote write failed.\n");
1060 (void) shutdown(cip
->dataSocket
, 2);
1063 #else /* NO_SIGNALS */
1064 nwrote
= write(cip
->dataSocket
, cp
, ntowrite
);
1066 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
1067 cip
->errNo
= result
= kErrSocketWriteFailed
;
1069 Error(cip
, kDoPerror
, "Lost data connection to remote host.\n");
1070 } else if (errno
== EINTR
) {
1073 cip
->errNo
= result
= kErrSocketWriteFailed
;
1074 Error(cip
, kDoPerror
, "Remote write failed.\n");
1076 (void) shutdown(cip
->dataSocket
, 2);
1079 #endif /* NO_SIGNALS */
1082 } while (ntowrite
> 0);
1083 FTPUpdateIOTimer(cip
);
1086 #endif /* ASCII_TRANSLATION */
1090 #if !defined(NO_SIGNALS)
1091 gCanBrokenDataJmp
= 0;
1092 #endif /* NO_SIGNALS */
1094 nread
= read(fd
, cp
, bufSize
);
1096 if (errno
== EINTR
) {
1099 result
= kErrReadFailed
;
1100 cip
->errNo
= kErrReadFailed
;
1101 Error(cip
, kDoPerror
, "Local read failed.\n");
1104 } else if (nread
== 0) {
1107 cip
->bytesTransferred
+= (longest_int
) nread
;
1109 #if !defined(NO_SIGNALS)
1110 gCanBrokenDataJmp
= 1;
1111 if (cip
->xferTimeout
> 0)
1112 (void) alarm(cip
->xferTimeout
);
1113 #endif /* NO_SIGNALS */
1115 if (! WaitForRemoteOutput(cip
)) { /* could set cancelXfer */
1116 cip
->errNo
= result
= kErrDataTimedOut
;
1117 Error(cip
, kDontPerror
, "Remote write timed out.\n");
1120 if (cip
->cancelXfer
> 0) {
1121 FTPAbortDataTransfer(cip
);
1122 result
= cip
->errNo
= kErrDataTransferAborted
;
1127 nwrote
= SWrite(cip
->dataSocket
, cp
, (size_t) nread
, (int) cip
->xferTimeout
, kNoFirstSelect
);
1129 if (nwrote
== kTimeoutErr
) {
1130 cip
->errNo
= result
= kErrDataTimedOut
;
1131 Error(cip
, kDontPerror
, "Remote write timed out.\n");
1132 } else if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
1133 cip
->errNo
= result
= kErrSocketWriteFailed
;
1135 Error(cip
, kDoPerror
, "Lost data connection to remote host.\n");
1136 } else if (errno
== EINTR
) {
1139 cip
->errNo
= result
= kErrSocketWriteFailed
;
1140 Error(cip
, kDoPerror
, "Remote write failed.\n");
1142 (void) shutdown(cip
->dataSocket
, 2);
1143 cip
->dataSocket
= -1;
1146 #else /* NO_SIGNALS */
1147 nwrote
= write(cip
->dataSocket
, cp
, nread
);
1149 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
1150 cip
->errNo
= result
= kErrSocketWriteFailed
;
1152 Error(cip
, kDoPerror
, "Lost data connection to remote host.\n");
1153 } else if (errno
== EINTR
) {
1156 cip
->errNo
= result
= kErrSocketWriteFailed
;
1157 Error(cip
, kDoPerror
, "Remote write failed.\n");
1159 (void) shutdown(cip
->dataSocket
, 2);
1160 cip
->dataSocket
= -1;
1163 #endif /* NO_SIGNALS */
1166 } while (nread
> 0);
1167 FTPUpdateIOTimer(cip
);
1173 (void) Fstat(fd
, &st
);
1177 if (shutdown(fd
, 1) == 0) {
1178 /* This looks very bizarre, since
1179 * we will be checking the socket
1180 * for readability here!
1182 * The reason for this is that we
1183 * want to be able to timeout a
1184 * small put. So, we close the
1185 * write end of the socket first,
1186 * which tells the server we're
1187 * done writing. We then wait
1188 * for the server to close down
1189 * the whole socket, which tells
1190 * us that the file was completed.
1192 (void) WaitForRemoteInput(cip
); /* Close could block. */
1196 #if !defined(NO_SIGNALS)
1197 gCanBrokenDataJmp
= 0;
1198 if (cip
->xferTimeout
> 0)
1200 #endif /* NO_SIGNALS */
1201 tmpResult
= FTPEndDataCmd(cip
, 1);
1202 if ((tmpResult
< 0) && (result
== kNoErr
)) {
1203 cip
->errNo
= result
= kErrSTORFailed
;
1205 FTPStopIOTimer(cip
);
1208 /* If they gave us a descriptor (fdtouse >= 0),
1209 * leave it open, otherwise we opened it, so
1210 * we need to dispose of it.
1216 if (result
== kNoErr
) {
1217 /* The store succeeded; If we were
1218 * uploading to a temporary file,
1219 * move the new file to the new name.
1223 if ((tmppfx
[0] != '\0') || (tmpsfx
[0] != '\0')) {
1224 if ((result
= FTPRename(cip
, dstfile
, odstfile
)) < 0) {
1225 /* May fail if file was already there,
1226 * so delete the old one so we can move
1229 if (FTPDelete(cip
, odstfile
, kRecursiveNo
, kGlobNo
) == kNoErr
) {
1230 result
= FTPRename(cip
, dstfile
, odstfile
);
1232 Error(cip
, kDontPerror
, "Could not rename %s to %s: %s.\n", dstfile
, odstfile
, FTPStrError(cip
->errNo
));
1235 Error(cip
, kDontPerror
, "Could not delete old %s, so could not rename %s to that: %s\n", odstfile
, dstfile
, FTPStrError(cip
->errNo
));
1240 if (FTPUtime(cip
, odstfile
, st
.st_atime
, st
.st_mtime
, st
.st_ctime
) != kNoErr
) {
1241 if (cip
->errNo
!= kErrUTIMENotAvailable
)
1242 Error(cip
, kDontPerror
, "Could not preserve times for %s: %s.\n", odstfile
, FTPStrError(cip
->errNo
));
1245 if (deleteflag
== kDeleteYes
) {
1246 if (unlink(file
) < 0) {
1247 result
= cip
->errNo
= kErrLocalDeleteFailed
;
1252 #if !defined(NO_SIGNALS)
1253 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
1254 #endif /* NO_SIGNALS */
1264 const char *const file
,
1265 const char *const dstfile
,
1268 const int appendflag
,
1269 const char *const tmppfx
,
1270 const char *const tmpsfx
,
1271 const int resumeflag
,
1272 const int deleteflag
,
1273 const ConfirmResumeUploadProc resumeProc
,
1274 int UNUSED(reserved
))
1278 LIBNCFTP_USE_VAR(reserved
);
1280 return (kErrBadParameter
);
1281 if (strcmp(cip
->magic
, kLibraryMagic
))
1282 return (kErrBadMagic
);
1284 if ((dstfile
== NULL
) || (dstfile
[0] == '\0'))
1285 return (kErrBadParameter
);
1287 if ((file
== NULL
) || (file
[0] == '\0'))
1288 return (kErrBadParameter
);
1290 result
= FTPPutOneF(cip
, file
, dstfile
, xtype
, fdtouse
, appendflag
, tmppfx
, tmpsfx
, resumeflag
, deleteflag
, resumeProc
);
1292 } /* FTPPutOneFile3 */
1300 const char *const pattern
,
1301 const char *const dstdir1
,
1306 const char *const tmppfx
,
1307 const char *const tmpsfx
,
1308 const int resumeflag
,
1309 const int deleteflag
,
1310 const ConfirmResumeUploadProc resumeProc
,
1311 int UNUSED(reserved
))
1315 FileInfoPtr filePtr
;
1321 LIBNCFTP_USE_VAR(reserved
);
1323 return (kErrBadParameter
);
1324 if (strcmp(cip
->magic
, kLibraryMagic
))
1325 return (kErrBadMagic
);
1327 if (dstdir1
== NULL
) {
1330 dstdir
= STRNCPY(dstdir2
, dstdir1
);
1331 StrRemoveTrailingLocalPathDelim(dstdir2
);
1334 (void) FTPLocalGlob(cip
, &globList
, pattern
, doGlob
);
1335 if (recurse
== kRecursiveYes
) {
1336 appendflag
= kAppendNo
;
1337 (void) FTPLocalRecursiveFileList(cip
, &globList
, &files
);
1338 if (files
.first
== NULL
) {
1339 cip
->errNo
= kErrNoValidFilesSpecified
;
1340 return (kErrNoValidFilesSpecified
);
1342 (void) ComputeRNames(&files
, dstdir
, 0, 1);
1344 (void) LineListToFileInfoList(&globList
, &files
);
1345 (void) ComputeLNames(&files
, NULL
, NULL
, 1);
1346 (void) ComputeRNames(&files
, dstdir
, 0, 0);
1348 DisposeLineListContents(&globList
);
1351 for (filePtr
= files
.first
; filePtr
!= NULL
; filePtr
= filePtr
->next
) {
1352 PrintF(cip
, " R=%s, L=%s, 2=%s, size=%d, mdtm=%u, type=%c\n",
1355 filePtr
->rlinkto
? filePtr
->rlinkto
: "",
1357 (unsigned int) filePtr
->mdtm
,
1363 batchResult
= kNoErr
;
1364 for (filePtr
= files
.first
; filePtr
!= NULL
; filePtr
= filePtr
->next
) {
1365 if (cip
->connected
== 0) {
1366 if (batchResult
== kNoErr
)
1367 batchResult
= kErrRemoteHostClosedConnection
;
1370 if (filePtr
->type
== 'd') {
1372 StrRemoveTrailingLocalPathDelim(filePtr
->rname
);
1373 result
= FTPMkdir(cip
, filePtr
->rname
, kRecursiveNo
);
1374 if (result
!= kNoErr
)
1375 batchResult
= result
;
1377 } else if (filePtr
->type
== 'l') {
1379 /* no RFC way to create the link, though. */
1380 if ((filePtr
->rlinkto
!= NULL
) && (filePtr
->rlinkto
[0] != '\0'))
1381 (void) FTPSymlink(cip
, filePtr
->rname
, filePtr
->rlinkto
);
1383 } else if (recurse
!= kRecursiveYes
) {
1384 result
= FTPPutOneF(cip
, filePtr
->lname
, filePtr
->rname
, xtype
, -1, appendflag
, tmppfx
, tmpsfx
, resumeflag
, deleteflag
, resumeProc
);
1385 if (files
.nFileInfos
== 1) {
1386 if (result
!= kNoErr
)
1387 batchResult
= result
;
1389 if ((result
!= kNoErr
) && (result
!= kErrLocalFileNewer
) && (result
!= kErrRemoteFileNewer
) && (result
!= kErrLocalSameAsRemote
))
1390 batchResult
= result
;
1392 if (result
== kErrUserCanceled
)
1393 cip
->cancelXfer
= 1;
1394 if (cip
->cancelXfer
> 0)
1397 result
= FTPPutOneF(cip
, filePtr
->lname
, filePtr
->rname
, xtype
, -1, appendflag
, tmppfx
, tmpsfx
, resumeflag
, deleteflag
, resumeProc
);
1398 if (files
.nFileInfos
== 1) {
1399 if (result
!= kNoErr
)
1400 batchResult
= result
;
1402 if ((result
!= kNoErr
) && (result
!= kErrLocalFileNewer
) && (result
!= kErrRemoteFileNewer
) && (result
!= kErrLocalSameAsRemote
))
1403 batchResult
= result
;
1405 if (result
== kErrUserCanceled
)
1406 cip
->cancelXfer
= 1;
1407 if (cip
->cancelXfer
> 0)
1411 DisposeFileInfoListContents(&files
);
1412 if (batchResult
< 0)
1413 cip
->errNo
= batchResult
;
1414 return (batchResult
);
1415 } /* FTPPutFiles3 */
1420 /* The purpose of this is to provide updates for the progress meters
1421 * during lags. Return zero if the operation timed-out.
1424 WaitForRemoteInput(const FTPCIPtr cip
)
1434 xferTimeout
= cip
->xferTimeout
;
1435 if (xferTimeout
< 1)
1438 fd
= cip
->dataSocket
;
1442 ocancelXfer
= cip
->cancelXfer
;
1446 while ((xferTimeout
<= 0) || (wsecs
< xferTimeout
)) {
1447 if ((cip
->cancelXfer
!= 0) && (ocancelXfer
== 0)) {
1448 /* leave cip->stalled -- could have been stalled and then canceled. */
1456 result
= select(fd
+ 1, SELECT_TYPE_ARG234
&ss
, NULL
, SELECT_TYPE_ARG234
&ss2
, &tv
);
1461 } else if (result
< 0) {
1462 if (result
!= EINTR
) {
1469 cip
->stalled
= wsecs
;
1471 FTPUpdateIOTimer(cip
);
1474 #if !defined(NO_SIGNALS)
1475 /* Shouldn't get here -- alarm() should have
1478 (void) kill(getpid(), SIGALRM
);
1479 #endif /* NO_SIGNALS */
1481 cip
->dataTimedOut
= 1;
1482 return (0); /* timed-out */
1483 } /* WaitForRemoteInput */
1488 /* Nice for UNIX, but not necessary otherwise. */
1492 OpenTar(const FTPCIPtr cip
, const char *const dstdir
, int *const pid
)
1501 if (access(TAR
, X_OK
) < 0) {
1502 /* Path to TAR is invalid. */
1506 if (pipe(pipe1
) < 0) {
1507 Error(cip
, kDoPerror
, "pipe to Tar failed");
1511 pid1
= (int) fork();
1513 (void) close(pipe1
[0]);
1514 (void) close(pipe1
[1]);
1516 } else if (pid1
== 0) {
1518 if ((dstdir
!= NULL
) && (dstdir
[0] != '\0') && (chdir(dstdir
) < 0)) {
1519 Error(cip
, kDoPerror
, "tar chdir to %s failed", dstdir
);
1522 (void) close(pipe1
[1]); /* close write end */
1523 (void) dup2(pipe1
[0], 0); /* use read end on stdin */
1524 (void) close(pipe1
[0]);
1526 for (i
=3; i
<256; i
++)
1529 argv
[0] = (char *) "tar";
1530 argv
[1] = (char *) "xpf";
1531 argv
[2] = (char *) "-";
1534 (void) execv(TAR
, argv
);
1541 (void) close(pipe1
[0]); /* close read end */
1542 return (pipe1
[1]); /* use write end */
1549 FTPGetOneTarF(const FTPCIPtr cip
, const char *file
, const char *const dstdir
)
1554 volatile int result
;
1558 const char *volatile vfile
;
1561 volatile FTPSigProc osigpipe
;
1562 volatile FTPCIPtr vcip
;
1566 char *volatile basecp
;
1571 if ((file
[0] == '\0') || ((file
[0] == '/') && (file
[1] == '\0'))) {
1573 * We can't do that, because "get /.tar"
1574 * or "get .tar" does not work.
1576 result
= kErrOpenFailed
;
1577 cip
->errNo
= kErrOpenFailed
;
1581 if (FTPCmd(cip
, "MDTM %s.tar", file
) == 2) {
1582 /* Better not use this method since there is
1583 * no way to tell if the server would use the
1584 * existing .tar or do a new one on the fly.
1586 result
= kErrOpenFailed
;
1587 cip
->errNo
= kErrOpenFailed
;
1591 basecp
= strrchr(file
, '/');
1593 basecp
= strrchr(file
, '\\');
1594 if (basecp
!= NULL
) {
1595 /* Need to cd to the parent directory and get it
1598 if (FTPGetCWD(cip
, savedCwd
, sizeof(savedCwd
)) != 0) {
1599 result
= kErrOpenFailed
;
1600 cip
->errNo
= kErrOpenFailed
;
1603 result
= FTPChdir(cip
, file
);
1604 if (result
!= kNoErr
) {
1607 result
= FTPChdir(cip
, "..");
1608 if (result
!= kNoErr
) {
1609 (void) FTPChdir(cip
, savedCwd
);
1615 fd
= OpenTar(cip
, dstdir
, &pid
);
1617 result
= kErrOpenFailed
;
1618 cip
->errNo
= kErrOpenFailed
;
1620 (void) FTPChdir(cip
, savedCwd
);
1629 osigpipe
= (volatile FTPSigProc
) signal(SIGPIPE
, BrokenData
);
1632 gCanBrokenDataJmp
= 0;
1634 #ifdef HAVE_SIGSETJMP
1635 sj
= sigsetjmp(gBrokenDataJmp
, 1);
1637 sj
= setjmp(gBrokenDataJmp
);
1638 #endif /* HAVE_SIGSETJMP */
1641 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
1642 FTPShutdownHost(vcip
);
1644 (void) signal(SIGPIPE
, SIG_IGN
);
1648 if ((waitpid(pid
, &status
, 0) < 0) && (errno
!= EINTR
))
1651 if ((wait(&status
) < 0) && (errno
!= EINTR
))
1654 if (WIFEXITED(status
) || WIFSIGNALED(status
))
1658 (void) FTPChdir(cip
, savedCwd
);
1659 vcip
->errNo
= kErrRemoteHostClosedConnection
;
1660 return(vcip
->errNo
);
1662 gCanBrokenDataJmp
= 1;
1664 #endif /* NO_SIGNALS */
1666 tmpResult
= FTPStartDataCmd(cip
, kNetReading
, kTypeBinary
, (longest_int
) 0, "RETR %s.tar", vfile
);
1668 if (tmpResult
< 0) {
1670 if (result
== kErrGeneric
)
1671 result
= kErrRETRFailed
;
1672 cip
->errNo
= result
;
1675 (void) signal(SIGPIPE
, SIG_IGN
);
1680 if ((waitpid(pid
, &status
, 0) < 0) && (errno
!= EINTR
))
1683 if ((wait(&status
) < 0) && (errno
!= EINTR
))
1686 if (WIFEXITED(status
) || WIFSIGNALED(status
))
1691 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
1694 (void) FTPChdir(cip
, savedCwd
);
1700 bufSize
= cip
->bufSize
;
1702 FTPInitIOTimer(cip
);
1703 cip
->lname
= vfile
; /* could be NULL */
1705 FTPStartIOTimer(cip
);
1709 if (! WaitForRemoteInput(cip
)) { /* could set cancelXfer */
1710 cip
->errNo
= result
= kErrDataTimedOut
;
1711 Error(cip
, kDontPerror
, "Remote read timed out.\n");
1714 if (cip
->cancelXfer
> 0) {
1715 FTPAbortDataTransfer(cip
);
1716 result
= cip
->errNo
= kErrDataTransferAborted
;
1719 #if !defined(NO_SIGNALS)
1720 gCanBrokenDataJmp
= 1;
1721 if (cip
->xferTimeout
> 0)
1722 (void) alarm(cip
->xferTimeout
);
1723 #endif /* NO_SIGNALS */
1725 nread
= SRead(cip
->dataSocket
, buf
, bufSize
, (int) cip
->xferTimeout
, kFullBufferNotRequired
|kNoFirstSelect
);
1726 if (nread
== kTimeoutErr
) {
1727 cip
->errNo
= result
= kErrDataTimedOut
;
1728 Error(cip
, kDontPerror
, "Remote read timed out.\n");
1730 } else if (nread
< 0) {
1733 Error(cip
, kDoPerror
, "Remote read failed.\n");
1734 result
= kErrSocketReadFailed
;
1735 cip
->errNo
= kErrSocketReadFailed
;
1737 } else if (nread
== 0) {
1741 nread
= read(cip
->dataSocket
, buf
, bufSize
);
1745 Error(cip
, kDoPerror
, "Remote read failed.\n");
1746 result
= kErrSocketReadFailed
;
1747 cip
->errNo
= kErrSocketReadFailed
;
1749 } else if (nread
== 0) {
1752 gCanBrokenDataJmp
= 0;
1755 nwrote
= write(fd
, buf
, nread
);
1756 if (nwrote
!= nread
) {
1757 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
1758 result
= kErrWriteFailed
;
1759 cip
->errNo
= kErrWriteFailed
;
1762 Error(cip
, kDoPerror
, "Local write failed.\n");
1763 result
= kErrWriteFailed
;
1764 cip
->errNo
= kErrWriteFailed
;
1768 cip
->bytesTransferred
+= (longest_int
) nread
;
1769 FTPUpdateIOTimer(cip
);
1772 #if !defined(NO_SIGNALS)
1773 if (cip
->xferTimeout
> 0)
1775 gCanBrokenDataJmp
= 0;
1776 #endif /* NO_SIGNALS */
1781 if ((waitpid(pid
, &status
, 0) < 0) && (errno
!= EINTR
))
1784 if ((wait(&status
) < 0) && (errno
!= EINTR
))
1787 if (WIFEXITED(status
) || WIFSIGNALED(status
))
1791 tmpResult
= FTPEndDataCmd(cip
, 1);
1792 if ((tmpResult
< 0) && (result
== 0)) {
1793 result
= kErrRETRFailed
;
1794 cip
->errNo
= kErrRETRFailed
;
1796 FTPStopIOTimer(cip
);
1797 #if !defined(NO_SIGNALS)
1798 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
1801 if ((result
== 0) && (cip
->bytesTransferred
== 0)) {
1802 result
= kErrOpenFailed
;
1803 cip
->errNo
= kErrOpenFailed
;
1806 (void) FTPChdir(cip
, savedCwd
);
1808 } /* FTPGetOneTarF */
1819 const char *const file
,
1820 const char *dstfile
,
1823 longest_int expectedSize
,
1825 const int resumeflag
,
1826 const int appendflag
,
1827 const int deleteflag
,
1828 const ConfirmResumeDownloadProc resumeProc
)
1833 volatile int result
;
1836 #if ASCII_TRANSLATION
1841 volatile longest_int startPoint
= 0;
1844 #if !defined(NO_SIGNALS)
1845 volatile FTPSigProc osigpipe
;
1846 volatile FTPCIPtr vcip
;
1847 volatile int vfd
, vfdtouse
;
1849 #endif /* NO_SIGNALS */
1850 volatile int created
= 0;
1851 int zaction
= kConfirmResumeProcSaidBestGuess
;
1856 if (cip
->buf
== NULL
) {
1857 Error(cip
, kDoPerror
, "Transfer buffer not allocated.\n");
1858 cip
->errNo
= kErrNoBuf
;
1859 return (cip
->errNo
);
1866 /* Only ask for extended information
1867 * if we have the name of the file
1868 * and we didn't already have the
1871 * Always ask for the modification time,
1872 * because even if it was passed in it
1873 * may not be accurate. This is often
1874 * the case when it came from an ls
1875 * listing, in which the local time
1876 * zone could be a factor.
1880 AutomaticallyUseASCIIModeDependingOnExtension(cip
, file
, &xtype
);
1881 if (expectedSize
== kSizeUnknown
) {
1882 (void) FTPFileSizeAndModificationTime(cip
, file
, &expectedSize
, xtype
, &mdtm
);
1884 (void) FTPFileModificationTime(cip
, file
, &mdtm
);
1887 /* For Get, we can't recover very well if it turns out restart
1888 * didn't work, so check beforehand.
1890 if ((resumeflag
== kResumeYes
) || (resumeProc
!= NoConfirmResumeDownloadProc
)) {
1891 if (cip
->hasREST
== kCommandAvailabilityUnknown
) {
1892 (void) FTPSetTransferType(cip
, kTypeBinary
);
1893 if (SetStartOffset(cip
, (longest_int
) 1) == kNoErr
) {
1894 /* Now revert -- we still may not end up
1897 SetStartOffset(cip
, (longest_int
) -1);
1902 if (appendflag
== kAppendYes
) {
1903 zaction
= kConfirmResumeProcSaidAppend
;
1904 } else if (cip
->hasREST
== kCommandNotAvailable
) {
1905 zaction
= kConfirmResumeProcSaidOverwrite
;
1906 } else if (resumeflag
== kResumeYes
) {
1907 zaction
= kConfirmResumeProcSaidBestGuess
;
1909 zaction
= kConfirmResumeProcSaidOverwrite
;
1912 statrc
= Stat(dstfile
, &st
);
1914 if (resumeProc
!= NULL
) {
1915 zaction
= (*resumeProc
)(
1917 (longest_int
) st
.st_size
,
1926 if (zaction
== kConfirmResumeProcSaidBestGuess
) {
1927 if (expectedSize
!= kSizeUnknown
) {
1928 /* We know the size of the remote file,
1929 * and we have a local file too.
1931 * Try and decide if we need to get
1932 * the entire file, or just part of it.
1935 startPoint
= (longest_int
) st
.st_size
;
1936 zaction
= kConfirmResumeProcSaidResume
;
1938 /* If the local file exists and has a recent
1939 * modification time (< 12 hours) and
1940 * the remote file's modtime is not recent,
1941 * then heuristically conclude that the
1942 * local modtime should not be trusted
1943 * (i.e. user killed the process before
1944 * the local modtime could be preserved).
1947 if (mdtm
!= kModTimeUnknown
) {
1949 if ((st
.st_mtime
> now
) || (((now
- st
.st_mtime
) < 46200) && ((now
- mdtm
) >= 46200)))
1953 if ((mdtm
== kModTimeUnknown
) || (noMdtmCheck
!= 0)) {
1954 /* Can't use the timestamps as an aid. */
1955 if (startPoint
== expectedSize
) {
1956 /* Don't go to all the trouble of downloading nothing. */
1957 cip
->errNo
= kErrLocalSameAsRemote
;
1958 if (deleteflag
== kDeleteYes
)
1959 (void) FTPDelete(cip
, file
, kRecursiveNo
, kGlobNo
);
1960 return (cip
->errNo
);
1961 } else if (startPoint
> expectedSize
) {
1962 /* Panic; odds are the file we have
1963 * was a different file altogether,
1964 * since it is larger than the
1965 * remote copy. Re-do it all.
1967 zaction
= kConfirmResumeProcSaidOverwrite
;
1968 } /* else resume at startPoint */
1969 } else if ((mdtm
== st
.st_mtime
) || (mdtm
== (st
.st_mtime
- 1)) || (mdtm
== (st
.st_mtime
+ 1))) {
1970 /* File has the same time.
1971 * Note: Windows' file timestamps can be off by one second!
1973 if (startPoint
== expectedSize
) {
1974 /* Don't go to all the trouble of downloading nothing. */
1975 cip
->errNo
= kErrLocalSameAsRemote
;
1976 if (deleteflag
== kDeleteYes
)
1977 (void) FTPDelete(cip
, file
, kRecursiveNo
, kGlobNo
);
1978 return (cip
->errNo
);
1979 } else if (startPoint
> expectedSize
) {
1980 /* Panic; odds are the file we have
1981 * was a different file altogether,
1982 * since it is larger than the
1983 * remote copy. Re-do it all.
1985 zaction
= kConfirmResumeProcSaidOverwrite
;
1987 /* We have a file by the same time,
1988 * but smaller start point. Leave
1989 * the startpoint as is since it
1990 * is most likely valid.
1993 } else if (mdtm
< st
.st_mtime
) {
1994 /* Remote file is older than
1995 * local file. Don't overwrite
1998 cip
->errNo
= kErrLocalFileNewer
;
1999 return (cip
->errNo
);
2000 } else /* if (mdtm > st.st_mtime) */ {
2001 /* File has a newer timestamp
2002 * altogether, assume the remote
2003 * file is an entirely new file
2004 * and replace ours with it.
2006 zaction
= kConfirmResumeProcSaidOverwrite
;
2009 zaction
= kConfirmResumeProcSaidOverwrite
;
2013 zaction
= kConfirmResumeProcSaidOverwrite
;
2016 if (zaction
== kConfirmResumeProcSaidCancel
) {
2017 /* User wants to cancel this file and any
2018 * remaining in batch.
2020 cip
->errNo
= kErrUserCanceled
;
2021 return (cip
->errNo
);
2022 } else if (zaction
== kConfirmResumeProcSaidSkip
) {
2023 /* Nothing done, but not an error. */
2024 if (deleteflag
== kDeleteYes
)
2025 (void) FTPDelete(cip
, file
, kRecursiveNo
, kGlobNo
);
2027 } else if (zaction
== kConfirmResumeProcSaidResume
) {
2028 /* Resume; proc set the startPoint. */
2029 if (startPoint
== expectedSize
) {
2030 /* Don't go to all the trouble of downloading nothing. */
2031 /* Nothing done, but not an error. */
2032 if (deleteflag
== kDeleteYes
)
2033 (void) FTPDelete(cip
, file
, kRecursiveNo
, kGlobNo
);
2035 } else if (startPoint
> expectedSize
) {
2036 /* Cannot set start point past end of remote file */
2037 cip
->errNo
= result
= kErrSetStartPoint
;
2040 fd
= Open(dstfile
, O_WRONLY
|O_APPEND
|O_BINARY
, 00666);
2041 } else if (zaction
== kConfirmResumeProcSaidAppend
) {
2042 /* leave startPoint at zero, we will append everything. */
2043 startPoint
= (longest_int
) 0;
2044 fd
= Open(dstfile
, O_WRONLY
|O_CREAT
|O_APPEND
|O_BINARY
, 00666);
2045 } else /* if (zaction == kConfirmResumeProcSaidOverwrite) */ {
2047 startPoint
= (longest_int
) 0;
2048 fd
= Open(dstfile
, O_WRONLY
|O_CREAT
|O_TRUNC
|O_BINARY
, 00666);
2052 Error(cip
, kDoPerror
, "Cannot open local file %s for writing.\n", dstfile
);
2053 result
= kErrOpenFailed
;
2054 cip
->errNo
= kErrOpenFailed
;
2058 if ((expectedSize
== (longest_int
) 0) && (startPoint
<= (longest_int
) 0) && (zaction
!= kConfirmResumeProcSaidOverwrite
)) {
2059 /* Don't go to all the trouble of downloading nothing. */
2060 #if defined(WIN32) || defined(_WINDOWS)
2061 /* Note: Windows doesn't allow zero-size files. */
2062 (void) write(fd
, "\r\n", 2);
2065 if (mdtm
!= kModTimeUnknown
) {
2067 (void) time(&ut
.actime
);
2069 (void) utime(dstfile
, &ut
);
2071 if (deleteflag
== kDeleteYes
)
2072 (void) FTPDelete(cip
, file
, kRecursiveNo
, kGlobNo
);
2079 if ((cip
->numDownloads
== 0) && (cip
->dataSocketRBufSize
> 0)) {
2080 /* If dataSocketSBufSize is non-zero, it means you
2081 * want to explicitly try to set the size of the
2082 * socket's I/O buffer.
2084 * If it is zero, it means you want to just use the
2085 * TCP stack's default value, which is typically
2086 * between 8 and 64 kB.
2088 * If you try to set the buffer larger than 64 kB,
2089 * the TCP stack should try to use RFC 1323 to
2090 * negotiate "TCP Large Windows" which may yield
2091 * significant performance gains.
2093 if (cip
->hasRETRBUFSIZE
== kCommandAvailable
)
2094 (void) FTPCmd(cip
, "SITE RETRBUFSIZE %lu", (unsigned long) cip
->dataSocketRBufSize
);
2095 else if (cip
->hasRBUFSIZ
== kCommandAvailable
)
2096 (void) FTPCmd(cip
, "SITE RBUFSIZ %lu", (unsigned long) cip
->dataSocketRBufSize
);
2097 else if (cip
->hasRBUFSZ
== kCommandAvailable
)
2098 (void) FTPCmd(cip
, "SITE RBUFSZ %lu", (unsigned long) cip
->dataSocketRBufSize
);
2099 else if (cip
->hasBUFSIZE
== kCommandAvailable
)
2100 (void) FTPCmd(cip
, "SITE BUFSIZE %lu", (unsigned long) cip
->dataSocketSBufSize
);
2104 #else /* NO_SIGNALS */
2108 osigpipe
= (volatile FTPSigProc
) signal(SIGPIPE
, BrokenData
);
2111 gCanBrokenDataJmp
= 0;
2113 #ifdef HAVE_SIGSETJMP
2114 sj
= sigsetjmp(gBrokenDataJmp
, 1);
2116 sj
= setjmp(gBrokenDataJmp
);
2117 #endif /* HAVE_SIGSETJMP */
2120 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
2124 FTPShutdownHost(vcip
);
2125 vcip
->errNo
= kErrRemoteHostClosedConnection
;
2126 return(vcip
->errNo
);
2128 gCanBrokenDataJmp
= 1;
2129 #endif /* NO_SIGNALS */
2131 tmpResult
= FTPStartDataCmd(cip
, kNetReading
, xtype
, startPoint
, "RETR %s", file
);
2133 if (tmpResult
< 0) {
2135 if (result
== kErrGeneric
)
2136 result
= kErrRETRFailed
;
2137 cip
->errNo
= result
;
2140 if ((created
!= 0) && (appendflag
== kAppendNo
) && (cip
->startPoint
== 0))
2141 (void) unlink(dstfile
);
2143 #if !defined(NO_SIGNALS)
2144 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
2145 #endif /* NO_SIGNALS */
2149 if ((startPoint
!= 0) && (cip
->startPoint
== 0)) {
2150 /* Remote could not or would not set the start offset
2151 * to what we wanted.
2153 * So now we have to undo our seek.
2155 if (Lseek(fd
, (off_t
) 0, SEEK_SET
) != (off_t
) 0) {
2156 cip
->errNo
= kErrLseekFailed
;
2160 #if !defined(NO_SIGNALS)
2161 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
2162 #endif /* NO_SIGNALS */
2163 return (cip
->errNo
);
2169 bufSize
= cip
->bufSize
;
2171 FTPInitIOTimer(cip
);
2173 (void) time(&ut
.actime
);
2175 cip
->expectedSize
= expectedSize
;
2176 cip
->lname
= dstfile
; /* could be NULL */
2179 cip
->useProgressMeter
= 0;
2180 FTPStartIOTimer(cip
);
2182 #if ASCII_TRANSLATION
2183 if (xtype
== kTypeAscii
) {
2186 if (! WaitForRemoteInput(cip
)) { /* could set cancelXfer */
2187 cip
->errNo
= result
= kErrDataTimedOut
;
2188 Error(cip
, kDontPerror
, "Remote read timed out.\n");
2191 if (cip
->cancelXfer
> 0) {
2192 FTPAbortDataTransfer(cip
);
2193 result
= cip
->errNo
= kErrDataTransferAborted
;
2197 if (cip
->bytesTransferred
> 0) {
2198 cip
->cancelXfer
= 1;
2199 FTPAbortDataTransfer(cip
);
2200 result
= cip
->errNo
= kErrDataTransferAborted
;
2203 #endif /* TESTING_ABOR */
2205 nread
= SRead(cip
->dataSocket
, buf
, bufSize
, (int) cip
->xferTimeout
, kFullBufferNotRequired
|kNoFirstSelect
);
2206 if (nread
== kTimeoutErr
) {
2207 cip
->errNo
= result
= kErrDataTimedOut
;
2208 Error(cip
, kDontPerror
, "Remote read timed out.\n");
2210 } else if (nread
< 0) {
2211 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
2212 result
= cip
->errNo
= kErrSocketReadFailed
;
2214 Error(cip
, kDoPerror
, "Lost data connection to remote host.\n");
2215 } else if (errno
== EINTR
) {
2218 Error(cip
, kDoPerror
, "Remote read failed.\n");
2219 result
= kErrSocketReadFailed
;
2220 cip
->errNo
= kErrSocketReadFailed
;
2223 } else if (nread
== 0) {
2227 gCanBrokenDataJmp
= 1;
2228 if (cip
->xferTimeout
> 0)
2229 (void) alarm(cip
->xferTimeout
);
2230 nread
= read(cip
->dataSocket
, buf
, bufSize
);
2232 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
2233 result
= cip
->errNo
= kErrSocketReadFailed
;
2235 Error(cip
, kDoPerror
, "Lost data connection to remote host.\n");
2236 (void) shutdown(cip
->dataSocket
, 2);
2237 } else if (errno
== EINTR
) {
2240 result
= cip
->errNo
= kErrSocketReadFailed
;
2241 Error(cip
, kDoPerror
, "Remote read failed.\n");
2242 (void) shutdown(cip
->dataSocket
, 2);
2245 } else if (nread
== 0) {
2249 gCanBrokenDataJmp
= 0;
2250 #endif /* NO_SIGNALS */
2253 srclim
= src
+ nread
;
2255 dstlim
= dst
+ sizeof(outbuf
);
2256 while (src
< srclim
) {
2261 if (dst
>= dstlim
) {
2262 nwrote
= write(fd
, outbuf
, (size_t) (dst
- outbuf
));
2263 if (nwrote
== (int) (dst
- outbuf
)) {
2266 } else if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
2267 result
= kErrWriteFailed
;
2268 cip
->errNo
= kErrWriteFailed
;
2270 (void) shutdown(cip
->dataSocket
, 2);
2273 Error(cip
, kDoPerror
, "Local write failed.\n");
2274 result
= kErrWriteFailed
;
2275 cip
->errNo
= kErrWriteFailed
;
2276 (void) shutdown(cip
->dataSocket
, 2);
2283 nwrote
= write(fd
, outbuf
, (size_t) (dst
- outbuf
));
2284 if (nwrote
!= (int) (dst
- outbuf
)) {
2285 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
2286 result
= kErrWriteFailed
;
2287 cip
->errNo
= kErrWriteFailed
;
2289 (void) shutdown(cip
->dataSocket
, 2);
2292 Error(cip
, kDoPerror
, "Local write failed.\n");
2293 result
= kErrWriteFailed
;
2294 cip
->errNo
= kErrWriteFailed
;
2295 (void) shutdown(cip
->dataSocket
, 2);
2301 if (mdtm
!= kModTimeUnknown
) {
2302 (void) utime(dstfile
, &ut
);
2304 cip
->bytesTransferred
+= (longest_int
) nread
;
2305 FTPUpdateIOTimer(cip
);
2308 #endif /* ASCII_TRANSLATION */
2312 if (! WaitForRemoteInput(cip
)) { /* could set cancelXfer */
2313 cip
->errNo
= result
= kErrDataTimedOut
;
2314 Error(cip
, kDontPerror
, "Remote read timed out.\n");
2317 if (cip
->cancelXfer
> 0) {
2318 FTPAbortDataTransfer(cip
);
2319 result
= cip
->errNo
= kErrDataTransferAborted
;
2323 if (cip
->bytesTransferred
> 0) {
2324 cip
->cancelXfer
= 1;
2325 FTPAbortDataTransfer(cip
);
2326 result
= cip
->errNo
= kErrDataTransferAborted
;
2329 #endif /* TESTING_ABOR */
2331 nread
= SRead(cip
->dataSocket
, buf
, bufSize
, (int) cip
->xferTimeout
, kFullBufferNotRequired
|kNoFirstSelect
);
2332 if (nread
== kTimeoutErr
) {
2333 cip
->errNo
= result
= kErrDataTimedOut
;
2334 Error(cip
, kDontPerror
, "Remote read timed out.\n");
2336 } else if (nread
< 0) {
2337 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
2338 result
= cip
->errNo
= kErrSocketReadFailed
;
2340 Error(cip
, kDoPerror
, "Lost data connection to remote host.\n");
2341 } else if (errno
== EINTR
) {
2344 Error(cip
, kDoPerror
, "Remote read failed.\n");
2345 result
= kErrSocketReadFailed
;
2346 cip
->errNo
= kErrSocketReadFailed
;
2349 } else if (nread
== 0) {
2353 gCanBrokenDataJmp
= 1;
2354 if (cip
->xferTimeout
> 0)
2355 (void) alarm(cip
->xferTimeout
);
2356 nread
= read(cip
->dataSocket
, buf
, bufSize
);
2358 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
2359 result
= cip
->errNo
= kErrSocketReadFailed
;
2361 Error(cip
, kDoPerror
, "Lost data connection to remote host.\n");
2362 } else if (errno
== EINTR
) {
2365 result
= cip
->errNo
= kErrSocketReadFailed
;
2366 Error(cip
, kDoPerror
, "Remote read failed.\n");
2368 (void) shutdown(cip
->dataSocket
, 2);
2370 } else if (nread
== 0) {
2373 gCanBrokenDataJmp
= 0;
2374 #endif /* NO_SIGNALS */
2376 nwrote
= write(fd
, buf
, nread
);
2377 if (nwrote
!= nread
) {
2378 if ((gGotBrokenData
!= 0) || (errno
== EPIPE
)) {
2379 result
= kErrWriteFailed
;
2380 cip
->errNo
= kErrWriteFailed
;
2383 Error(cip
, kDoPerror
, "Local write failed.\n");
2384 result
= kErrWriteFailed
;
2385 cip
->errNo
= kErrWriteFailed
;
2387 (void) shutdown(cip
->dataSocket
, 2);
2391 /* Ugggh... do this after each write operation
2392 * so it minimizes the chance of a user killing
2393 * the process before we reset the timestamps.
2395 if (mdtm
!= kModTimeUnknown
) {
2396 (void) utime(dstfile
, &ut
);
2398 cip
->bytesTransferred
+= (longest_int
) nread
;
2399 FTPUpdateIOTimer(cip
);
2403 #if ASCII_TRANSLATION
2407 #if !defined(NO_SIGNALS)
2408 if (cip
->xferTimeout
> 0)
2410 gCanBrokenDataJmp
= 0;
2411 #endif /* NO_SIGNALS */
2414 /* If they gave us a descriptor (fdtouse >= 0),
2415 * leave it open, otherwise we opened it, so
2416 * we need to close it.
2422 tmpResult
= FTPEndDataCmd(cip
, 1);
2423 if ((tmpResult
< 0) && (result
== 0)) {
2424 result
= kErrRETRFailed
;
2425 cip
->errNo
= kErrRETRFailed
;
2427 FTPStopIOTimer(cip
);
2428 #if !defined(NO_SIGNALS)
2429 (void) signal(SIGPIPE
, (FTPSigProc
) osigpipe
);
2430 #endif /* NO_SIGNALS */
2432 if ((mdtm
!= kModTimeUnknown
) && (cip
->bytesTransferred
> 0)) {
2433 (void) utime(dstfile
, &ut
);
2436 if (result
== kNoErr
) {
2437 cip
->numDownloads
++;
2439 if (deleteflag
== kDeleteYes
) {
2440 result
= FTPDelete(cip
, file
, kRecursiveNo
, kGlobNo
);
2453 const char *const file
,
2454 const char *const dstfile
,
2457 const int resumeflag
,
2458 const int appendflag
,
2459 const int deleteflag
,
2460 const ConfirmResumeDownloadProc resumeProc
,
2461 int UNUSED(reserved
))
2465 LIBNCFTP_USE_VAR(reserved
);
2467 return (kErrBadParameter
);
2468 if (strcmp(cip
->magic
, kLibraryMagic
))
2469 return (kErrBadMagic
);
2471 if ((file
== NULL
) || (file
[0] == '\0'))
2472 return (kErrBadParameter
);
2474 if ((dstfile
== NULL
) || (dstfile
[0] == '\0'))
2475 return (kErrBadParameter
);
2478 result
= FTPGetOneF(cip
, file
, dstfile
, xtype
, fdtouse
, kSizeUnknown
, kModTimeUnknown
, resumeflag
, appendflag
, deleteflag
, resumeProc
);
2480 } /* FTPGetOneFile3 */
2488 const char *pattern1
,
2489 const char *const dstdir1
,
2493 const int resumeflag
,
2495 const int deleteflag
,
2497 const ConfirmResumeDownloadProc resumeProc
,
2498 int UNUSED(reserved
))
2503 FileInfoPtr filePtr
;
2509 const char *pattern
;
2510 char *pattern2
, *dstdir2
;
2515 LIBNCFTP_USE_VAR(reserved
);
2517 return (kErrBadParameter
);
2518 if (strcmp(cip
->magic
, kLibraryMagic
))
2519 return (kErrBadMagic
);
2520 if (pattern1
== NULL
)
2521 return (kErrBadParameter
);
2526 if (dstdir1
== NULL
) {
2529 dstdir2
= StrDup(dstdir1
);
2530 if (dstdir2
== NULL
) {
2531 errRc
= kErrMallocFailed
;
2534 StrRemoveTrailingLocalPathDelim(dstdir2
);
2538 pattern2
= StrDup(pattern1
);
2539 if (pattern2
== NULL
) {
2540 errRc
= kErrMallocFailed
;
2543 StrRemoveTrailingSlashes(pattern2
);
2546 if (pattern
[0] == '\0') {
2547 if (recurse
== kRecursiveNo
) {
2548 errRc
= kErrBadParameter
;
2553 } else if (strcmp(pattern
, ".") == 0) {
2554 if (recurse
== kRecursiveNo
) {
2555 errRc
= kErrBadParameter
;
2560 if (recurse
== kRecursiveYes
)
2561 appendflag
= kAppendNo
;
2563 batchResult
= FTPRemoteGlob(cip
, &globList
, pattern
, doGlob
);
2564 if (batchResult
!= kNoErr
) {
2565 errRc
= batchResult
;
2569 cip
->cancelXfer
= 0; /* should already be zero */
2571 for (itemPtr
= globList
.first
; itemPtr
!= NULL
; itemPtr
= itemPtr
->next
) {
2572 if ((recurse
== kRecursiveYes
) && (FTPIsDir(cip
, itemPtr
->line
) > 0)) {
2574 if ((tarflag
== kTarYes
) && (xtype
== kTypeBinary
) && (appendflag
== kAppendNo
) && (deleteflag
== kDeleteNo
) && (FTPGetOneTarF(cip
, itemPtr
->line
, dstdir
) == kNoErr
)) {
2579 (void) FTPRemoteRecursiveFileList1(cip
, itemPtr
->line
, &files
);
2580 (void) ComputeLNames(&files
, itemPtr
->line
, dstdir
, 1);
2583 recurse1
= kRecursiveNo
;
2584 (void) LineToFileInfoList(itemPtr
, &files
);
2585 (void) ComputeRNames(&files
, ".", 0, 1);
2586 (void) ComputeLNames(&files
, NULL
, dstdir
, 0);
2588 if (cip
->cancelXfer
> 0) {
2589 DisposeFileInfoListContents(&files
);
2594 for (filePtr
= files
.first
; filePtr
!= NULL
; filePtr
= filePtr
->next
) {
2595 PrintF(cip
, " R=%s, L=%s, 2=%s, size=%d, mdtm=%u, type=%c\n",
2598 filePtr
->rlinkto
? filePtr
->rlinkto
: "",
2600 (unsigned int) filePtr
->mdtm
,
2607 for (filePtr
= files
.first
; filePtr
!= NULL
; filePtr
= filePtr
->next
) {
2608 if (cip
->connected
== 0) {
2609 if (batchResult
== kNoErr
)
2610 batchResult
= kErrRemoteHostClosedConnection
;
2613 if (filePtr
->type
== 'd') {
2614 #if defined(WIN32) || defined(_WINDOWS)
2615 (void) MkDirs(filePtr
->lname
, 00777);
2617 (void) mkdir(filePtr
->lname
, 00777);
2619 } else if (filePtr
->type
== 'l') {
2620 /* skip it -- we do that next pass. */
2621 } else if (recurse1
!= kRecursiveYes
) {
2622 result
= FTPGetOneF(cip
, filePtr
->rname
, filePtr
->lname
, xtype
, -1, filePtr
->size
, filePtr
->mdtm
, resumeflag
, appendflag
, deleteflag
, resumeProc
);
2623 if (files
.nFileInfos
== 1) {
2624 if (result
!= kNoErr
)
2625 batchResult
= result
;
2627 if ((result
!= kNoErr
) && (result
!= kErrLocalFileNewer
) && (result
!= kErrRemoteFileNewer
) && (result
!= kErrLocalSameAsRemote
))
2628 batchResult
= result
;
2630 if (result
== kErrUserCanceled
)
2631 cip
->cancelXfer
= 1;
2632 if (cip
->cancelXfer
> 0)
2635 ldir
= filePtr
->lname
;
2636 cp
= StrRFindLocalPathDelim(ldir
);
2639 if (! IsLocalPathDelim(*cp
)) {
2648 if (MkDirs(ldir
, 00777) < 0) {
2649 Error(cip
, kDoPerror
, "Could not create local directory \"%s\"\n", ldir
);
2650 batchResult
= kErrGeneric
;
2657 result
= FTPGetOneF(cip
, filePtr
->rname
, filePtr
->lname
, xtype
, -1, filePtr
->size
, filePtr
->mdtm
, resumeflag
, appendflag
, deleteflag
, resumeProc
);
2659 if (files
.nFileInfos
== 1) {
2660 if (result
!= kNoErr
)
2661 batchResult
= result
;
2663 if ((result
!= kNoErr
) && (result
!= kErrLocalFileNewer
) && (result
!= kErrRemoteFileNewer
) && (result
!= kErrLocalSameAsRemote
))
2664 batchResult
= result
;
2666 if (result
== kErrUserCanceled
)
2667 cip
->cancelXfer
= 1;
2668 if (cip
->cancelXfer
> 0)
2672 if (cip
->cancelXfer
> 0) {
2673 DisposeFileInfoListContents(&files
);
2678 for (filePtr
= files
.first
; filePtr
!= NULL
; filePtr
= filePtr
->next
) {
2679 if (filePtr
->type
== 'l') {
2680 (void) unlink(filePtr
->lname
);
2681 if (symlink(filePtr
->rlinkto
, filePtr
->lname
) < 0) {
2682 Error(cip
, kDoPerror
, "Could not symlink %s to %s\n", filePtr
->rlinkto
, filePtr
->lname
);
2683 /* Note: not worth setting batchResult */
2687 #endif /* HAVE_SYMLINK */
2690 DisposeFileInfoListContents(&files
);
2693 DisposeLineListContents(&globList
);
2694 if (batchResult
< 0)
2695 cip
->errNo
= batchResult
;
2696 errRc
= batchResult
;
2699 if (dstdir2
!= NULL
)
2701 if (pattern2
!= NULL
)
2704 } /* FTPGetFiles3 */
2709 /*------------------------- wrappers for old routines ----------------------*/
2712 FTPGetOneFile(const FTPCIPtr cip
, const char *const file
, const char *const dstfile
)
2714 return (FTPGetOneFile3(cip
, file
, dstfile
, kTypeBinary
, -1, kResumeNo
, kAppendNo
, kDeleteNo
, (ConfirmResumeDownloadProc
) 0, 0));
2715 } /* FTPGetOneFile */
2721 FTPGetOneFile2(const FTPCIPtr cip
, const char *const file
, const char *const dstfile
, const int xtype
, const int fdtouse
, const int resumeflag
, const int appendflag
)
2723 return (FTPGetOneFile3(cip
, file
, dstfile
, xtype
, fdtouse
, resumeflag
, appendflag
, kDeleteNo
, (ConfirmResumeDownloadProc
) 0, 0));
2724 } /* FTPGetOneFile2 */
2730 FTPGetFiles(const FTPCIPtr cip
, const char *const pattern
, const char *const dstdir
, const int recurse
, const int doGlob
)
2732 return (FTPGetFiles3(cip
, pattern
, dstdir
, recurse
, doGlob
, kTypeBinary
, kResumeNo
, kAppendNo
, kDeleteNo
, kTarYes
, (ConfirmResumeDownloadProc
) 0, 0));
2739 FTPGetFiles2(const FTPCIPtr cip
, const char *const pattern
, const char *const dstdir
, const int recurse
, const int doGlob
, const int xtype
, const int resumeflag
, const int appendflag
)
2741 return (FTPGetFiles3(cip
, pattern
, dstdir
, recurse
, doGlob
, xtype
, resumeflag
, appendflag
, kDeleteNo
, kTarYes
, (ConfirmResumeDownloadProc
) 0, 0));
2742 } /* FTPGetFiles2 */
2748 FTPGetOneFileAscii(const FTPCIPtr cip
, const char *const file
, const char *const dstfile
)
2750 return (FTPGetOneFile3(cip
, file
, dstfile
, kTypeAscii
, -1, kResumeNo
, kAppendNo
, kDeleteNo
, (ConfirmResumeDownloadProc
) 0, 0));
2751 } /* FTPGetOneFileAscii */
2757 FTPGetFilesAscii(const FTPCIPtr cip
, const char *const pattern
, const char *const dstdir
, const int recurse
, const int doGlob
)
2759 return (FTPGetFiles3(cip
, pattern
, dstdir
, recurse
, doGlob
, kTypeAscii
, kResumeNo
, kAppendNo
, kDeleteNo
, kTarNo
, (ConfirmResumeDownloadProc
) 0, 0));
2760 } /* FTPGetFilesAscii */
2766 FTPPutOneFile(const FTPCIPtr cip
, const char *const file
, const char *const dstfile
)
2768 return (FTPPutOneFile3(cip
, file
, dstfile
, kTypeBinary
, -1, 0, NULL
, NULL
, kResumeNo
, kDeleteNo
, NoConfirmResumeUploadProc
, 0));
2769 } /* FTPPutOneFile */
2775 FTPPutOneFile2(const FTPCIPtr cip
, const char *const file
, const char *const dstfile
, const int xtype
, const int fdtouse
, const int appendflag
, const char *const tmppfx
, const char *const tmpsfx
)
2777 return (FTPPutOneFile3(cip
, file
, dstfile
, xtype
, fdtouse
, appendflag
, tmppfx
, tmpsfx
, kResumeNo
, kDeleteNo
, NoConfirmResumeUploadProc
, 0));
2778 } /* FTPPutOneFile2 */
2784 FTPPutFiles(const FTPCIPtr cip
, const char *const pattern
, const char *const dstdir
, const int recurse
, const int doGlob
)
2786 return (FTPPutFiles3(cip
, pattern
, dstdir
, recurse
, doGlob
, kTypeBinary
, 0, NULL
, NULL
, kResumeNo
, kDeleteNo
, NoConfirmResumeUploadProc
, 0));
2793 FTPPutFiles2(const FTPCIPtr cip
, const char *const pattern
, const char *const dstdir
, const int recurse
, const int doGlob
, const int xtype
, const int appendflag
, const char *const tmppfx
, const char *const tmpsfx
)
2795 return (FTPPutFiles3(cip
, pattern
, dstdir
, recurse
, doGlob
, xtype
, appendflag
, tmppfx
, tmpsfx
, kResumeNo
, kDeleteNo
, NoConfirmResumeUploadProc
, 0));
2796 } /* FTPPutFiles2 */
2802 FTPPutOneFileAscii(const FTPCIPtr cip
, const char *const file
, const char *const dstfile
)
2804 return (FTPPutOneFile3(cip
, file
, dstfile
, kTypeAscii
, -1, 0, NULL
, NULL
, kResumeNo
, kDeleteNo
, NoConfirmResumeUploadProc
, 0));
2805 } /* FTPPutOneFileAscii */
2811 FTPPutFilesAscii(const FTPCIPtr cip
, const char *const pattern
, const char *const dstdir
, const int recurse
, const int doGlob
)
2813 return (FTPPutFiles3(cip
, pattern
, dstdir
, recurse
, doGlob
, kTypeAscii
, 0, NULL
, NULL
, kResumeNo
, kDeleteNo
, NoConfirmResumeUploadProc
, 0));
2814 } /* FTPPutFilesAscii */
2819 FTPListToMemory(const FTPCIPtr cip
, const char *const pattern
, const LineListPtr llines
, const char *const lsflags
)
2821 return (FTPListToMemory2(cip
, pattern
, llines
, lsflags
, 1, (int *) 0));
2822 } /* FTPListToMemory */