- Start rosapps rearrange and cleanup process.
[reactos.git] / rosapps / applications / net / ncftp / libncftp / io.c
1 /* io.c
2 *
3 * Copyright (c) 1996-2001 Mike Gleason, NCEMRSoft.
4 * All rights reserved.
5 *
6 */
7
8 #include "syshdrs.h"
9
10 static int gGotBrokenData = 0;
11
12 #if defined(WIN32) || defined(_WINDOWS)
13 # define ASCII_TRANSLATION 0
14 #endif
15
16 #ifndef ASCII_TRANSLATION
17 # define ASCII_TRANSLATION 1
18 #endif
19
20 #if !defined(NO_SIGNALS) && (USE_SIO || !defined(SIGALRM) || !defined(SIGPIPE) || !defined(SIGINT))
21 # define NO_SIGNALS 1
22 #endif
23
24 #ifndef NO_SIGNALS
25
26 #ifdef HAVE_SIGSETJMP
27 static sigjmp_buf gBrokenDataJmp;
28 #else
29 static jmp_buf gBrokenDataJmp;
30 #endif /* HAVE_SIGSETJMP */
31 static int gCanBrokenDataJmp = 0;
32
33 #endif /* NO_SIGNALS */
34
35
36 #ifndef O_BINARY
37 /* Needed for platforms using different EOLN sequence (i.e. DOS) */
38 # ifdef _O_BINARY
39 # define O_BINARY _O_BINARY
40 # else
41 # define O_BINARY 0
42 # endif
43 #endif
44
45 static int WaitForRemoteInput(const FTPCIPtr cip);
46 static int WaitForRemoteOutput(const FTPCIPtr cip);
47
48
49 #ifndef NO_SIGNALS
50
51 static void
52 BrokenData(int signum)
53 {
54 gGotBrokenData = signum;
55 if (gCanBrokenDataJmp != 0) {
56 gCanBrokenDataJmp = 0;
57 #ifdef HAVE_SIGSETJMP
58 siglongjmp(gBrokenDataJmp, 1);
59 #else
60 longjmp(gBrokenDataJmp, 1);
61 #endif /* HAVE_SIGSETJMP */
62 }
63 } /* BrokenData */
64
65 #endif /* NO_SIGNALS */
66
67
68
69
70 void
71 FTPInitIOTimer(const FTPCIPtr cip)
72 {
73 cip->bytesTransferred = (longest_int) 0;
74 cip->expectedSize = kSizeUnknown;
75 cip->mdtm = kModTimeUnknown;
76 cip->rname = NULL;
77 cip->lname = NULL;
78 cip->kBytesPerSec = -1.0;
79 cip->percentCompleted = -1.0;
80 cip->sec = -1.0;
81 cip->secLeft = -1.0;
82 cip->nextProgressUpdate = 0;
83 cip->stalled = 0;
84 cip->dataTimedOut = 0;
85 cip->useProgressMeter = 1;
86 (void) gettimeofday(&cip->t0, NULL);
87 } /* FTPInitIOTimer */
88
89
90
91
92 void
93 FTPStartIOTimer(const FTPCIPtr cip)
94 {
95 (void) gettimeofday(&cip->t0, NULL);
96 if (cip->progress != (FTPProgressMeterProc) 0)
97 (*cip->progress)(cip, kPrInitMsg);
98 } /* FTPStartIOTimer */
99
100
101
102
103 void
104 FTPUpdateIOTimer(const FTPCIPtr cip)
105 {
106 double sec;
107 struct timeval *t0, t1;
108 time_t now;
109
110 (void) time(&now);
111 if (now < cip->nextProgressUpdate)
112 return;
113 now += 1;
114 cip->nextProgressUpdate = now;
115
116 (void) gettimeofday(&t1, NULL);
117 t0 = &cip->t0;
118
119 if (t0->tv_usec > t1.tv_usec) {
120 t1.tv_usec += 1000000;
121 t1.tv_sec--;
122 }
123 sec = ((double) (t1.tv_usec - t0->tv_usec) * 0.000001)
124 + (t1.tv_sec - t0->tv_sec);
125 if (sec > 0.0) {
126 cip->kBytesPerSec = ((double) cip->bytesTransferred) / (1024.0 * sec);
127 } else {
128 cip->kBytesPerSec = -1.0;
129 }
130 if (cip->expectedSize == kSizeUnknown) {
131 cip->percentCompleted = -1.0;
132 cip->secLeft = -1.0;
133 } else if (cip->expectedSize <= 0) {
134 cip->percentCompleted = 100.0;
135 cip->secLeft = 0.0;
136 } else {
137 cip->percentCompleted = ((double) (100.0 * (cip->bytesTransferred + cip->startPoint))) / ((double) cip->expectedSize);
138 if (cip->percentCompleted >= 100.0) {
139 cip->percentCompleted = 100.0;
140 cip->secLeft = 0.0;
141 } else if (cip->percentCompleted <= 0.0) {
142 cip->secLeft = 999.0;
143 }
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)
147 cip->secLeft = 0.0;
148 }
149 }
150 cip->sec = sec;
151 if ((cip->progress != (FTPProgressMeterProc) 0) && (cip->useProgressMeter != 0))
152 (*cip->progress)(cip, kPrUpdateMsg);
153 } /* FTPUpdateIOTimer */
154
155
156
157
158 void
159 FTPStopIOTimer(const FTPCIPtr cip)
160 {
161 cip->nextProgressUpdate = 0; /* force last update */
162 FTPUpdateIOTimer(cip);
163 if (cip->progress != (FTPProgressMeterProc) 0)
164 (*cip->progress)(cip, kPrEndMsg);
165 } /* FTPStopIOTimer */
166
167
168
169
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.
172 */
173 int
174 FTPList(const FTPCIPtr cip, const int outfd, const int longMode, const char *const lsflag)
175 {
176 const char *cmd;
177 char line[512];
178 char secondaryBuf[768];
179 #ifndef NO_SIGNALS
180 char *secBufPtr, *secBufLimit;
181 int nread;
182 volatile int result;
183 #else /* NO_SIGNALS */
184 SReadlineInfo lsSrl;
185 int result;
186 #endif /* NO_SIGNALS */
187
188 if (cip == NULL)
189 return (kErrBadParameter);
190 if (strcmp(cip->magic, kLibraryMagic))
191 return (kErrBadMagic);
192
193 cmd = (longMode != 0) ? "LIST" : "NLST";
194 if ((lsflag == NULL) || (lsflag[0] == '\0')) {
195 result = FTPStartDataCmd(cip, kNetReading, kTypeAscii, (longest_int) 0, "%s", cmd);
196 } else {
197 result = FTPStartDataCmd(cip, kNetReading, kTypeAscii, (longest_int) 0, "%s %s", cmd, lsflag);
198 }
199
200
201 #ifdef NO_SIGNALS
202
203 if (result == 0) {
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");
209 return (result);
210 }
211
212 for (;;) {
213 result = SReadline(&lsSrl, line, sizeof(line) - 2);
214 if (result == kTimeoutErr) {
215 /* timeout */
216 Error(cip, kDontPerror, "Could not directory listing data -- timed out.\n");
217 cip->errNo = kErrDataTimedOut;
218 return (cip->errNo);
219 } else if (result == 0) {
220 /* end of listing -- done */
221 cip->numListings++;
222 break;
223 } else if (result < 0) {
224 /* error */
225 Error(cip, kDoPerror, "Could not read directory listing data");
226 result = kErrLISTFailed;
227 cip->errNo = kErrLISTFailed;
228 break;
229 }
230
231 (void) write(outfd, line, strlen(line));
232 }
233
234 DisposeSReadlineInfo(&lsSrl);
235 if (FTPEndDataCmd(cip, 1) < 0) {
236 result = kErrLISTFailed;
237 cip->errNo = kErrLISTFailed;
238 }
239 } else if (result == kErrGeneric) {
240 result = kErrLISTFailed;
241 cip->errNo = kErrLISTFailed;
242 }
243
244
245 #else /* NO_SIGNALS */
246
247 if (result == 0) {
248 /* This line sets the buffer pointer so that the first thing
249 * BufferGets will do is reset and fill the buffer using
250 * real I/O.
251 */
252 secBufPtr = secondaryBuf + sizeof(secondaryBuf);
253 secBufLimit = (char *) 0;
254
255 for (;;) {
256 if (cip->xferTimeout > 0)
257 (void) alarm(cip->xferTimeout);
258 nread = BufferGets(line, sizeof(line), cip->dataSocket, secondaryBuf, &secBufPtr, &secBufLimit, sizeof(secondaryBuf));
259 if (nread <= 0) {
260 if (nread < 0)
261 break;
262 } else {
263 cip->bytesTransferred += (longest_int) nread;
264 (void) STRNCAT(line, "\n");
265 (void) write(outfd, line, strlen(line));
266 }
267 }
268 if (cip->xferTimeout > 0)
269 (void) alarm(0);
270 result = FTPEndDataCmd(cip, 1);
271 if (result < 0) {
272 result = kErrLISTFailed;
273 cip->errNo = kErrLISTFailed;
274 }
275 result = kNoErr;
276 cip->numListings++;
277 } else if (result == kErrGeneric) {
278 result = kErrLISTFailed;
279 cip->errNo = kErrLISTFailed;
280 }
281 #endif /* NO_SIGNALS */
282 return (result);
283 } /* FTPList */
284
285
286
287
288 static void
289 FTPRequestMlsOptions(const FTPCIPtr cip)
290 {
291 int f;
292 char optstr[128];
293 size_t optstrlen;
294
295 if (cip->usedMLS == 0) {
296 /* First MLSD/MLST ? */
297 cip->usedMLS = 1;
298
299 f = cip->mlsFeatures & kPreferredMlsOpts;
300 optstr[0] = '\0';
301
302 /* TYPE */
303 if ((f & kMlsOptType) != 0) {
304 STRNCAT(optstr, "type;");
305 }
306
307 /* SIZE */
308 if ((f & kMlsOptSize) != 0) {
309 STRNCAT(optstr, "size;");
310 }
311
312 /* MODTIME */
313 if ((f & kMlsOptModify) != 0) {
314 STRNCAT(optstr, "modify;");
315 }
316
317 /* MODE */
318 if ((f & kMlsOptUNIXmode) != 0) {
319 STRNCAT(optstr, "UNIX.mode;");
320 }
321
322 /* PERM */
323 if ((f & kMlsOptPerm) != 0) {
324 STRNCAT(optstr, "perm;");
325 }
326
327 /* OWNER */
328 if ((f & kMlsOptUNIXowner) != 0) {
329 STRNCAT(optstr, "UNIX.owner;");
330 }
331
332 /* UID */
333 if ((f & kMlsOptUNIXuid) != 0) {
334 STRNCAT(optstr, "UNIX.uid;");
335 }
336
337 /* GROUP */
338 if ((f & kMlsOptUNIXgroup) != 0) {
339 STRNCAT(optstr, "UNIX.group;");
340 }
341
342 /* GID */
343 if ((f & kMlsOptUNIXgid) != 0) {
344 STRNCAT(optstr, "UNIX.gid;");
345 }
346
347 /* UNIQUE */
348 if ((f & kMlsOptUnique) != 0) {
349 STRNCAT(optstr, "unique;");
350 }
351
352 /* Tell the server what we prefer. */
353 optstrlen = strlen(optstr);
354 if (optstrlen > 0) {
355 if (optstr[optstrlen - 1] == ';')
356 optstr[optstrlen - 1] = '\0';
357 (void) FTPCmd(cip, "OPTS MLST %s", optstr);
358 }
359 }
360 } /* FTPRequestMlsOptions */
361
362
363
364
365 int
366 FTPListToMemory2(const FTPCIPtr cip, const char *const pattern, const LineListPtr llines, const char *const lsflags, const int blankLines, int *const tryMLSD)
367 {
368 char secondaryBuf[768];
369 char line[512];
370 char lsflags1[128];
371 const char *command = "NLST";
372 const char *scp;
373 char *dcp, *lim;
374 #ifndef NO_SIGNALS
375 char *secBufPtr, *secBufLimit;
376 volatile FTPSigProc osigpipe;
377 volatile FTPCIPtr vcip;
378 int sj;
379 int nread;
380 volatile int result;
381 #else /* NO_SIGNALS */
382 SReadlineInfo lsSrl;
383 int result;
384 #endif /* NO_SIGNALS */
385
386 if (cip == NULL)
387 return (kErrBadParameter);
388 if (strcmp(cip->magic, kLibraryMagic))
389 return (kErrBadMagic);
390
391 if ((llines == NULL) || (pattern == NULL) || (lsflags == NULL))
392 return (kErrBadParameter);
393
394 if ((tryMLSD != (int *) 0) && (*tryMLSD != 0) && (cip->hasMLSD == kCommandAvailable)) {
395 command = "MLSD";
396 if ((lsflags[0] == '-') && (strchr(lsflags, 'd') != NULL) && (cip->hasMLST == kCommandAvailable))
397 command = "MLST";
398 lsflags1[0] = '\0';
399 FTPRequestMlsOptions(cip);
400 } else {
401 /* Not using MLSD. */
402 if (tryMLSD != (int *) 0)
403 *tryMLSD = 0;
404 if (lsflags[0] == '-') {
405 /* See if we should use LIST instead. */
406 scp = lsflags + 1;
407 dcp = lsflags1;
408 lim = lsflags1 + sizeof(lsflags1) - 2;
409 for (; *scp != '\0'; scp++) {
410 if (*scp == 'l') {
411 /* do not add the 'l' */
412 command = "LIST";
413 } else if (dcp < lim) {
414 if (dcp == lsflags1)
415 *dcp++ = '-';
416 *dcp++ = *scp;
417 }
418 }
419 *dcp = '\0';
420 } else {
421 (void) STRNCPY(lsflags1, lsflags);
422 }
423 }
424
425 InitLineList(llines);
426
427 result = FTPStartDataCmd(
428 cip,
429 kNetReading,
430 kTypeAscii,
431 (longest_int) 0,
432 "%s%s%s%s%s",
433 command,
434 (lsflags1[0] == '\0') ? "" : " ",
435 lsflags1,
436 (pattern[0] == '\0') ? "" : " ",
437 pattern
438 );
439
440 #ifdef NO_SIGNALS
441
442 if (result == 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");
448 return (result);
449 }
450
451 for (;;) {
452 result = SReadline(&lsSrl, line, sizeof(line) - 1);
453 if (result == kTimeoutErr) {
454 /* timeout */
455 Error(cip, kDontPerror, "Could not directory listing data -- timed out.\n");
456 cip->errNo = kErrDataTimedOut;
457 return (cip->errNo);
458 } else if (result == 0) {
459 /* end of listing -- done */
460 cip->numListings++;
461 break;
462 } else if (result < 0) {
463 /* error */
464 Error(cip, kDoPerror, "Could not read directory listing data");
465 result = kErrLISTFailed;
466 cip->errNo = kErrLISTFailed;
467 break;
468 }
469
470 if (line[result - 1] == '\n')
471 line[result - 1] = '\0';
472
473 if ((blankLines == 0) && (result <= 1))
474 continue;
475
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 .. */
479
480 (void) AddLine(llines, line);
481 }
482
483 DisposeSReadlineInfo(&lsSrl);
484 if (FTPEndDataCmd(cip, 1) < 0) {
485 result = kErrLISTFailed;
486 cip->errNo = kErrLISTFailed;
487 }
488 } else if (result == kErrGeneric) {
489 result = kErrLISTFailed;
490 cip->errNo = kErrLISTFailed;
491 }
492
493
494 #else /* NO_SIGNALS */
495 vcip = cip;
496 osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
497
498 gGotBrokenData = 0;
499 gCanBrokenDataJmp = 0;
500
501 #ifdef HAVE_SIGSETJMP
502 sj = sigsetjmp(gBrokenDataJmp, 1);
503 #else
504 sj = setjmp(gBrokenDataJmp);
505 #endif /* HAVE_SIGSETJMP */
506
507 if (sj != 0) {
508 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
509 FTPShutdownHost(vcip);
510 vcip->errNo = kErrRemoteHostClosedConnection;
511 return(vcip->errNo);
512 }
513 gCanBrokenDataJmp = 1;
514
515 if (result == 0) {
516 /* This line sets the buffer pointer so that the first thing
517 * BufferGets will do is reset and fill the buffer using
518 * real I/O.
519 */
520 secBufPtr = secondaryBuf + sizeof(secondaryBuf);
521 secBufLimit = (char *) 0;
522 memset(secondaryBuf, 0, sizeof(secondaryBuf));
523
524 for (;;) {
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));
529 if (nread <= 0) {
530 if (nread < 0)
531 break;
532 if (blankLines != 0)
533 (void) AddLine(llines, line);
534 } else {
535 cip->bytesTransferred += (longest_int) nread;
536
537 if ((line[0] == '.') && ((line[1] == '\0') || ((line[1] == '.') && ((line[2] == '\0') || (iscntrl(line[2]))))))
538 continue; /* Skip . and .. */
539
540 (void) AddLine(llines, line);
541 }
542 }
543 if (cip->xferTimeout > 0)
544 (void) alarm(0);
545 result = FTPEndDataCmd(cip, 1);
546 if (result < 0) {
547 result = kErrLISTFailed;
548 cip->errNo = kErrLISTFailed;
549 }
550 result = kNoErr;
551 cip->numListings++;
552 } else if (result == kErrGeneric) {
553 result = kErrLISTFailed;
554 cip->errNo = kErrLISTFailed;
555 }
556 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
557 #endif /* NO_SIGNALS */
558 return (result);
559 } /* FTPListToMemory2 */
560
561
562
563
564 static void
565 AutomaticallyUseASCIIModeDependingOnExtension(const FTPCIPtr cip, const char *const pathName, int *const xtype)
566 {
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.
572 */
573 *xtype = kTypeAscii;
574 }
575 }
576 } /* AutomaticallyUseASCIIModeDependingOnExtension */
577
578
579
580
581 /* The purpose of this is to provide updates for the progress meters
582 * during lags. Return zero if the operation timed-out.
583 */
584 static int
585 WaitForRemoteOutput(const FTPCIPtr cip)
586 {
587 fd_set ss, ss2;
588 struct timeval tv;
589 int result;
590 int fd;
591 int wsecs;
592 int xferTimeout;
593 int ocancelXfer;
594
595 xferTimeout = cip->xferTimeout;
596 if (xferTimeout < 1)
597 return (1);
598
599 fd = cip->dataSocket;
600 if (fd < 0)
601 return (1);
602
603 ocancelXfer = cip->cancelXfer;
604 wsecs = 0;
605 cip->stalled = 0;
606
607 while ((xferTimeout <= 0) || (wsecs < xferTimeout)) {
608 if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) {
609 /* leave cip->stalled -- could have been stalled and then canceled. */
610 return (1);
611 }
612 FD_ZERO(&ss);
613 FD_SET(fd, &ss);
614 ss2 = ss;
615 tv.tv_sec = 1;
616 tv.tv_usec = 0;
617 result = select(fd + 1, NULL, SELECT_TYPE_ARG234 &ss, SELECT_TYPE_ARG234 &ss2, &tv);
618 if (result == 1) {
619 /* ready */
620 cip->stalled = 0;
621 return (1);
622 } else if (result < 0) {
623 if (errno != EINTR) {
624 perror("select");
625 cip->stalled = 0;
626 return (1);
627 }
628 } else {
629 wsecs++;
630 cip->stalled = wsecs;
631 }
632 FTPUpdateIOTimer(cip);
633 }
634
635 #if !defined(NO_SIGNALS)
636 /* Shouldn't get here -- alarm() should have
637 * went off by now.
638 */
639 (void) kill(getpid(), SIGALRM);
640 #endif /* NO_SIGNALS */
641
642 cip->dataTimedOut = 1;
643 return (0); /* timed-out */
644 } /* WaitForRemoteOutput */
645
646
647
648
649 static int
650 FTPPutOneF(
651 const FTPCIPtr cip,
652 const char *const file,
653 const char *volatile dstfile,
654 int xtype,
655 const int fdtouse,
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)
662 {
663 char *buf, *cp;
664 const char *cmd;
665 const char *odstfile;
666 size_t bufSize;
667 size_t l;
668 int tmpResult, result;
669 int nread, nwrote;
670 volatile int fd;
671 char dstfile2[512];
672 #if ASCII_TRANSLATION
673 char *src, *srclim, *dst;
674 int ntowrite;
675 char inbuf[256];
676 #endif
677 int fstatrc, statrc;
678 longest_int startPoint = 0;
679 struct Stat st;
680 time_t mdtm;
681 #if !defined(NO_SIGNALS)
682 int sj;
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;
689
690 if (cip->buf == NULL) {
691 Error(cip, kDoPerror, "Transfer buffer not allocated.\n");
692 cip->errNo = kErrNoBuf;
693 return (cip->errNo);
694 }
695
696 cip->usingTAR = 0;
697 if (fdtouse < 0) {
698 fd = Open(file, O_RDONLY|O_BINARY, 0);
699 if (fd < 0) {
700 Error(cip, kDoPerror, "Cannot open local file %s for reading.\n", file);
701 cip->errNo = kErrOpenFailed;
702 return (cip->errNo);
703 }
704 } else {
705 fd = fdtouse;
706 }
707
708 fstatrc = Fstat(fd, &st);
709 if ((fstatrc == 0) && (S_ISDIR(st.st_mode))) {
710 if (fdtouse < 0) {
711 (void) close(fd);
712 }
713 Error(cip, kDontPerror, "%s is a directory.\n", (file != NULL) ? file : "that");
714 cip->errNo = kErrOpenFailed;
715 return (cip->errNo);
716 }
717
718 /* For Put, we can't recover very well if it turns out restart
719 * didn't work, so check beforehand.
720 */
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
725 * doing it.
726 */
727 SetStartOffset(cip, (longest_int) -1);
728 }
729 }
730
731 if (fdtouse < 0) {
732 AutomaticallyUseASCIIModeDependingOnExtension(cip, dstfile, &xtype);
733 (void) FTPFileSizeAndModificationTime(cip, dstfile, &startPoint, xtype, &mdtm);
734
735 if (appendflag == kAppendYes) {
736 zaction = kConfirmResumeProcSaidAppend;
737 } else if (
738 (cip->hasREST == kCommandNotAvailable) ||
739 (xtype != kTypeBinary) ||
740 (fstatrc < 0)
741 ) {
742 zaction = kConfirmResumeProcSaidOverwrite;
743 } else if (resumeflag == kResumeYes) {
744 zaction = kConfirmResumeProcSaidBestGuess;
745 } else {
746 zaction = kConfirmResumeProcSaidOverwrite;
747 }
748
749 statrc = -1;
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.
753 */
754 statrc = 0;
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.
761 */
762 statrc = FTPFileExists2(cip, dstfile, 0, 0, 0, 1, 1);
763 }
764
765 if (
766 (resumeProc != NoConfirmResumeUploadProc) &&
767 (statrc == 0)
768 ) {
769 zaction = (*resumeProc)(file, (longest_int) st.st_size, st.st_mtime, &dstfile, startPoint, mdtm, &startPoint);
770 }
771
772 if (zaction == kConfirmResumeProcSaidCancel) {
773 /* User wants to cancel this file and any
774 * remaining in batch.
775 */
776 cip->errNo = kErrUserCanceled;
777 return (cip->errNo);
778 }
779
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.
785 *
786 * Note: Add one second fudge factor
787 * for Windows' file timestamps being
788 * imprecise to one second.
789 */
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;
796 } else {
797 zaction = kConfirmResumeProcSaidOverwrite;
798 }
799 }
800
801 if (zaction == kConfirmResumeProcSaidSkip) {
802 /* Nothing done, but not an error. */
803 if (fdtouse < 0) {
804 (void) close(fd);
805 }
806 if (deleteflag == kDeleteYes) {
807 if (unlink(file) < 0) {
808 cip->errNo = kErrLocalDeleteFailed;
809 return (cip->errNo);
810 }
811 }
812 return (kNoErr);
813 } else if (zaction == kConfirmResumeProcSaidResume) {
814 /* Resume; proc set the startPoint. */
815 if ((longest_int) st.st_size == startPoint) {
816 /* Already sent file, done. */
817 if (fdtouse < 0) {
818 (void) close(fd);
819 }
820
821 if (deleteflag == kDeleteYes) {
822 if (unlink(file) < 0) {
823 cip->errNo = kErrLocalDeleteFailed;
824 return (cip->errNo);
825 }
826 }
827 return (kNoErr);
828 } else if (Lseek(fd, (off_t) startPoint, SEEK_SET) != (off_t) -1) {
829 cip->startPoint = startPoint;
830 }
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;
837 }
838 }
839
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.
844 *
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.
848 *
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.
853 */
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.
862 */
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);
867 }
868
869 #ifdef NO_SIGNALS
870 vzaction = zaction;
871 #else /* NO_SIGNALS */
872 vcip = cip;
873 vfdtouse = fdtouse;
874 vfd = fd;
875 vzaction = zaction;
876 osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
877
878 gGotBrokenData = 0;
879 gCanBrokenDataJmp = 0;
880
881 #ifdef HAVE_SIGSETJMP
882 sj = sigsetjmp(gBrokenDataJmp, 1);
883 #else
884 sj = setjmp(gBrokenDataJmp);
885 #endif /* HAVE_SIGSETJMP */
886
887 if (sj != 0) {
888 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
889 if (vfdtouse < 0) {
890 (void) close(vfd);
891 }
892 FTPShutdownHost(vcip);
893 vcip->errNo = kErrRemoteHostClosedConnection;
894 return(vcip->errNo);
895 }
896 gCanBrokenDataJmp = 1;
897 #endif /* NO_SIGNALS */
898
899 if (vzaction == kConfirmResumeProcSaidAppend) {
900 cmd = "APPE";
901 tmppfx = ""; /* Can't use that here. */
902 tmpsfx = "";
903 } else {
904 cmd = "STOR";
905 if (tmppfx == NULL)
906 tmppfx = "";
907 if (tmpsfx == NULL)
908 tmpsfx = "";
909 }
910
911 odstfile = dstfile;
912 if ((tmppfx[0] != '\0') || (tmpsfx[0] != '\0')) {
913 cp = strrchr(dstfile, '/');
914 if (cp == NULL)
915 cp = strrchr(dstfile, '\\');
916 if (cp == NULL) {
917 (void) STRNCPY(dstfile2, tmppfx);
918 (void) STRNCAT(dstfile2, dstfile);
919 (void) STRNCAT(dstfile2, tmpsfx);
920 } else {
921 cp++;
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);
928 }
929 dstfile = dstfile2;
930 }
931
932 tmpResult = FTPStartDataCmd(
933 cip,
934 kNetWriting,
935 xtype,
936 startPoint,
937 "%s %s",
938 cmd,
939 dstfile
940 );
941
942 if (tmpResult < 0) {
943 cip->errNo = tmpResult;
944 if (fdtouse < 0) {
945 (void) close(fd);
946 }
947 #if !defined(NO_SIGNALS)
948 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
949 #endif /* NO_SIGNALS */
950 return (cip->errNo);
951 }
952
953 if ((startPoint != 0) && (cip->startPoint == 0)) {
954 /* Remote could not or would not set the start offset
955 * to what we wanted.
956 *
957 * So now we have to undo our seek.
958 */
959 if (Lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) {
960 cip->errNo = kErrLseekFailed;
961 if (fdtouse < 0) {
962 (void) close(fd);
963 }
964 #if !defined(NO_SIGNALS)
965 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
966 #endif /* NO_SIGNALS */
967 return (cip->errNo);
968 }
969 startPoint = 0;
970 }
971
972 result = kNoErr;
973 buf = cip->buf;
974 bufSize = cip->bufSize;
975
976 FTPInitIOTimer(cip);
977 if ((fstatrc == 0) && (S_ISREG(st.st_mode) != 0)) {
978 cip->expectedSize = (longest_int) st.st_size;
979 cip->mdtm = st.st_mtime;
980 }
981 cip->lname = file; /* could be NULL */
982 cip->rname = odstfile;
983 if (fdtouse >= 0)
984 cip->useProgressMeter = 0;
985 FTPStartIOTimer(cip);
986
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.
990 */
991
992 #if ASCII_TRANSLATION
993 if (xtype == kTypeAscii) {
994 /* ascii */
995 for (;;) {
996 #if !defined(NO_SIGNALS)
997 gCanBrokenDataJmp = 0;
998 #endif /* NO_SIGNALS */
999 nread = read(fd, inbuf, sizeof(inbuf));
1000 if (nread < 0) {
1001 if (errno == EINTR) {
1002 continue;
1003 } else {
1004 result = kErrReadFailed;
1005 cip->errNo = kErrReadFailed;
1006 Error(cip, kDoPerror, "Local read failed.\n");
1007 }
1008 break;
1009 } else if (nread == 0) {
1010 break;
1011 }
1012 cip->bytesTransferred += (longest_int) nread;
1013
1014 #if !defined(NO_SIGNALS)
1015 gCanBrokenDataJmp = 1;
1016 #endif /* NO_SIGNALS */
1017 src = inbuf;
1018 srclim = src + nread;
1019 dst = cip->buf; /* must be 2x sizeof inbuf or more. */
1020 while (src < srclim) {
1021 if (*src == '\n')
1022 *dst++ = '\r';
1023 *dst++ = *src++;
1024 }
1025 ntowrite = (size_t) (dst - cip->buf);
1026 cp = cip->buf;
1027
1028 #if !defined(NO_SIGNALS)
1029 if (cip->xferTimeout > 0)
1030 (void) alarm(cip->xferTimeout);
1031 #endif /* NO_SIGNALS */
1032 do {
1033 if (! WaitForRemoteOutput(cip)) { /* could set cancelXfer */
1034 cip->errNo = result = kErrDataTimedOut;
1035 Error(cip, kDontPerror, "Remote write timed out.\n");
1036 goto brk;
1037 }
1038 if (cip->cancelXfer > 0) {
1039 FTPAbortDataTransfer(cip);
1040 result = cip->errNo = kErrDataTransferAborted;
1041 goto brk;
1042 }
1043
1044 #ifdef NO_SIGNALS
1045 nwrote = SWrite(cip->dataSocket, cp, (size_t) ntowrite, (int) cip->xferTimeout, kNoFirstSelect);
1046 if (nwrote < 0) {
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;
1052 errno = EPIPE;
1053 Error(cip, kDoPerror, "Lost data connection to remote host.\n");
1054 } else if (errno == EINTR) {
1055 continue;
1056 } else {
1057 cip->errNo = result = kErrSocketWriteFailed;
1058 Error(cip, kDoPerror, "Remote write failed.\n");
1059 }
1060 (void) shutdown(cip->dataSocket, 2);
1061 goto brk;
1062 }
1063 #else /* NO_SIGNALS */
1064 nwrote = write(cip->dataSocket, cp, ntowrite);
1065 if (nwrote < 0) {
1066 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
1067 cip->errNo = result = kErrSocketWriteFailed;
1068 errno = EPIPE;
1069 Error(cip, kDoPerror, "Lost data connection to remote host.\n");
1070 } else if (errno == EINTR) {
1071 continue;
1072 } else {
1073 cip->errNo = result = kErrSocketWriteFailed;
1074 Error(cip, kDoPerror, "Remote write failed.\n");
1075 }
1076 (void) shutdown(cip->dataSocket, 2);
1077 goto brk;
1078 }
1079 #endif /* NO_SIGNALS */
1080 cp += nwrote;
1081 ntowrite -= nwrote;
1082 } while (ntowrite > 0);
1083 FTPUpdateIOTimer(cip);
1084 }
1085 } else
1086 #endif /* ASCII_TRANSLATION */
1087 {
1088 /* binary */
1089 for (;;) {
1090 #if !defined(NO_SIGNALS)
1091 gCanBrokenDataJmp = 0;
1092 #endif /* NO_SIGNALS */
1093 cp = buf;
1094 nread = read(fd, cp, bufSize);
1095 if (nread < 0) {
1096 if (errno == EINTR) {
1097 continue;
1098 } else {
1099 result = kErrReadFailed;
1100 cip->errNo = kErrReadFailed;
1101 Error(cip, kDoPerror, "Local read failed.\n");
1102 }
1103 break;
1104 } else if (nread == 0) {
1105 break;
1106 }
1107 cip->bytesTransferred += (longest_int) nread;
1108
1109 #if !defined(NO_SIGNALS)
1110 gCanBrokenDataJmp = 1;
1111 if (cip->xferTimeout > 0)
1112 (void) alarm(cip->xferTimeout);
1113 #endif /* NO_SIGNALS */
1114 do {
1115 if (! WaitForRemoteOutput(cip)) { /* could set cancelXfer */
1116 cip->errNo = result = kErrDataTimedOut;
1117 Error(cip, kDontPerror, "Remote write timed out.\n");
1118 goto brk;
1119 }
1120 if (cip->cancelXfer > 0) {
1121 FTPAbortDataTransfer(cip);
1122 result = cip->errNo = kErrDataTransferAborted;
1123 goto brk;
1124 }
1125
1126 #ifdef NO_SIGNALS
1127 nwrote = SWrite(cip->dataSocket, cp, (size_t) nread, (int) cip->xferTimeout, kNoFirstSelect);
1128 if (nwrote < 0) {
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;
1134 errno = EPIPE;
1135 Error(cip, kDoPerror, "Lost data connection to remote host.\n");
1136 } else if (errno == EINTR) {
1137 continue;
1138 } else {
1139 cip->errNo = result = kErrSocketWriteFailed;
1140 Error(cip, kDoPerror, "Remote write failed.\n");
1141 }
1142 (void) shutdown(cip->dataSocket, 2);
1143 cip->dataSocket = -1;
1144 goto brk;
1145 }
1146 #else /* NO_SIGNALS */
1147 nwrote = write(cip->dataSocket, cp, nread);
1148 if (nwrote < 0) {
1149 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
1150 cip->errNo = result = kErrSocketWriteFailed;
1151 errno = EPIPE;
1152 Error(cip, kDoPerror, "Lost data connection to remote host.\n");
1153 } else if (errno == EINTR) {
1154 continue;
1155 } else {
1156 cip->errNo = result = kErrSocketWriteFailed;
1157 Error(cip, kDoPerror, "Remote write failed.\n");
1158 }
1159 (void) shutdown(cip->dataSocket, 2);
1160 cip->dataSocket = -1;
1161 goto brk;
1162 }
1163 #endif /* NO_SIGNALS */
1164 cp += nwrote;
1165 nread -= nwrote;
1166 } while (nread > 0);
1167 FTPUpdateIOTimer(cip);
1168 }
1169 }
1170 brk:
1171
1172 if (fdtouse < 0) {
1173 (void) Fstat(fd, &st);
1174 }
1175
1176 if (fdtouse < 0) {
1177 if (shutdown(fd, 1) == 0) {
1178 /* This looks very bizarre, since
1179 * we will be checking the socket
1180 * for readability here!
1181 *
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.
1191 */
1192 (void) WaitForRemoteInput(cip); /* Close could block. */
1193 }
1194 }
1195
1196 #if !defined(NO_SIGNALS)
1197 gCanBrokenDataJmp = 0;
1198 if (cip->xferTimeout > 0)
1199 (void) alarm(0);
1200 #endif /* NO_SIGNALS */
1201 tmpResult = FTPEndDataCmd(cip, 1);
1202 if ((tmpResult < 0) && (result == kNoErr)) {
1203 cip->errNo = result = kErrSTORFailed;
1204 }
1205 FTPStopIOTimer(cip);
1206
1207 if (fdtouse < 0) {
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.
1211 */
1212 (void) close(fd);
1213 fd = -1;
1214 }
1215
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.
1220 */
1221 cip->numUploads++;
1222
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
1227 * over it.
1228 */
1229 if (FTPDelete(cip, odstfile, kRecursiveNo, kGlobNo) == kNoErr) {
1230 result = FTPRename(cip, dstfile, odstfile);
1231 if (result < 0) {
1232 Error(cip, kDontPerror, "Could not rename %s to %s: %s.\n", dstfile, odstfile, FTPStrError(cip->errNo));
1233 }
1234 } else {
1235 Error(cip, kDontPerror, "Could not delete old %s, so could not rename %s to that: %s\n", odstfile, dstfile, FTPStrError(cip->errNo));
1236 }
1237 }
1238 }
1239
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));
1243 }
1244
1245 if (deleteflag == kDeleteYes) {
1246 if (unlink(file) < 0) {
1247 result = cip->errNo = kErrLocalDeleteFailed;
1248 }
1249 }
1250 }
1251
1252 #if !defined(NO_SIGNALS)
1253 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
1254 #endif /* NO_SIGNALS */
1255 return (result);
1256 } /* FTPPutOneF */
1257
1258
1259
1260
1261 int
1262 FTPPutOneFile3(
1263 const FTPCIPtr cip,
1264 const char *const file,
1265 const char *const dstfile,
1266 const int xtype,
1267 const int fdtouse,
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))
1275 {
1276 int result;
1277
1278 LIBNCFTP_USE_VAR(reserved);
1279 if (cip == NULL)
1280 return (kErrBadParameter);
1281 if (strcmp(cip->magic, kLibraryMagic))
1282 return (kErrBadMagic);
1283
1284 if ((dstfile == NULL) || (dstfile[0] == '\0'))
1285 return (kErrBadParameter);
1286 if (fdtouse < 0) {
1287 if ((file == NULL) || (file[0] == '\0'))
1288 return (kErrBadParameter);
1289 }
1290 result = FTPPutOneF(cip, file, dstfile, xtype, fdtouse, appendflag, tmppfx, tmpsfx, resumeflag, deleteflag, resumeProc);
1291 return (result);
1292 } /* FTPPutOneFile3 */
1293
1294
1295
1296
1297 int
1298 FTPPutFiles3(
1299 const FTPCIPtr cip,
1300 const char *const pattern,
1301 const char *const dstdir1,
1302 const int recurse,
1303 const int doGlob,
1304 const int xtype,
1305 int appendflag,
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))
1312 {
1313 LineList globList;
1314 FileInfoList files;
1315 FileInfoPtr filePtr;
1316 int batchResult;
1317 int result;
1318 const char *dstdir;
1319 char dstdir2[512];
1320
1321 LIBNCFTP_USE_VAR(reserved);
1322 if (cip == NULL)
1323 return (kErrBadParameter);
1324 if (strcmp(cip->magic, kLibraryMagic))
1325 return (kErrBadMagic);
1326
1327 if (dstdir1 == NULL) {
1328 dstdir = NULL;
1329 } else {
1330 dstdir = STRNCPY(dstdir2, dstdir1);
1331 StrRemoveTrailingLocalPathDelim(dstdir2);
1332 }
1333
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);
1341 }
1342 (void) ComputeRNames(&files, dstdir, 0, 1);
1343 } else {
1344 (void) LineListToFileInfoList(&globList, &files);
1345 (void) ComputeLNames(&files, NULL, NULL, 1);
1346 (void) ComputeRNames(&files, dstdir, 0, 0);
1347 }
1348 DisposeLineListContents(&globList);
1349
1350 #if 0
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",
1353 filePtr->rname,
1354 filePtr->lname,
1355 filePtr->rlinkto ? filePtr->rlinkto : "",
1356 filePtr->size,
1357 (unsigned int) filePtr->mdtm,
1358 filePtr->type
1359 );
1360 }
1361 #endif
1362
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;
1368 break;
1369 }
1370 if (filePtr->type == 'd') {
1371 /* mkdir */
1372 StrRemoveTrailingLocalPathDelim(filePtr->rname);
1373 result = FTPMkdir(cip, filePtr->rname, kRecursiveNo);
1374 if (result != kNoErr)
1375 batchResult = result;
1376 #ifdef HAVE_SYMLINK
1377 } else if (filePtr->type == 'l') {
1378 /* symlink */
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);
1382 #endif
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;
1388 } else {
1389 if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
1390 batchResult = result;
1391 }
1392 if (result == kErrUserCanceled)
1393 cip->cancelXfer = 1;
1394 if (cip->cancelXfer > 0)
1395 break;
1396 } else {
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;
1401 } else {
1402 if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
1403 batchResult = result;
1404 }
1405 if (result == kErrUserCanceled)
1406 cip->cancelXfer = 1;
1407 if (cip->cancelXfer > 0)
1408 break;
1409 }
1410 }
1411 DisposeFileInfoListContents(&files);
1412 if (batchResult < 0)
1413 cip->errNo = batchResult;
1414 return (batchResult);
1415 } /* FTPPutFiles3 */
1416
1417
1418
1419
1420 /* The purpose of this is to provide updates for the progress meters
1421 * during lags. Return zero if the operation timed-out.
1422 */
1423 static int
1424 WaitForRemoteInput(const FTPCIPtr cip)
1425 {
1426 fd_set ss, ss2;
1427 struct timeval tv;
1428 int result;
1429 int fd;
1430 int wsecs;
1431 int xferTimeout;
1432 int ocancelXfer;
1433
1434 xferTimeout = cip->xferTimeout;
1435 if (xferTimeout < 1)
1436 return (1);
1437
1438 fd = cip->dataSocket;
1439 if (fd < 0)
1440 return (1);
1441
1442 ocancelXfer = cip->cancelXfer;
1443 wsecs = 0;
1444 cip->stalled = 0;
1445
1446 while ((xferTimeout <= 0) || (wsecs < xferTimeout)) {
1447 if ((cip->cancelXfer != 0) && (ocancelXfer == 0)) {
1448 /* leave cip->stalled -- could have been stalled and then canceled. */
1449 return (1);
1450 }
1451 FD_ZERO(&ss);
1452 FD_SET(fd, &ss);
1453 ss2 = ss;
1454 tv.tv_sec = 1;
1455 tv.tv_usec = 0;
1456 result = select(fd + 1, SELECT_TYPE_ARG234 &ss, NULL, SELECT_TYPE_ARG234 &ss2, &tv);
1457 if (result == 1) {
1458 /* ready */
1459 cip->stalled = 0;
1460 return (1);
1461 } else if (result < 0) {
1462 if (result != EINTR) {
1463 perror("select");
1464 cip->stalled = 0;
1465 return (1);
1466 }
1467 } else {
1468 wsecs++;
1469 cip->stalled = wsecs;
1470 }
1471 FTPUpdateIOTimer(cip);
1472 }
1473
1474 #if !defined(NO_SIGNALS)
1475 /* Shouldn't get here -- alarm() should have
1476 * went off by now.
1477 */
1478 (void) kill(getpid(), SIGALRM);
1479 #endif /* NO_SIGNALS */
1480
1481 cip->dataTimedOut = 1;
1482 return (0); /* timed-out */
1483 } /* WaitForRemoteInput */
1484
1485
1486
1487
1488 /* Nice for UNIX, but not necessary otherwise. */
1489 #ifdef TAR
1490
1491 static int
1492 OpenTar(const FTPCIPtr cip, const char *const dstdir, int *const pid)
1493 {
1494 int pipe1[2];
1495 int pid1;
1496 int i;
1497 char *argv[8];
1498
1499 *pid = -1;
1500
1501 if (access(TAR, X_OK) < 0) {
1502 /* Path to TAR is invalid. */
1503 return (-1);
1504 }
1505
1506 if (pipe(pipe1) < 0) {
1507 Error(cip, kDoPerror, "pipe to Tar failed");
1508 return (-1);
1509 }
1510
1511 pid1 = (int) fork();
1512 if (pid1 < 0) {
1513 (void) close(pipe1[0]);
1514 (void) close(pipe1[1]);
1515 return (-1);
1516 } else if (pid1 == 0) {
1517 /* Child */
1518 if ((dstdir != NULL) && (dstdir[0] != '\0') && (chdir(dstdir) < 0)) {
1519 Error(cip, kDoPerror, "tar chdir to %s failed", dstdir);
1520 exit(1);
1521 }
1522 (void) close(pipe1[1]); /* close write end */
1523 (void) dup2(pipe1[0], 0); /* use read end on stdin */
1524 (void) close(pipe1[0]);
1525
1526 for (i=3; i<256; i++)
1527 (void) close(i);
1528
1529 argv[0] = (char *) "tar";
1530 argv[1] = (char *) "xpf";
1531 argv[2] = (char *) "-";
1532 argv[3] = NULL;
1533
1534 (void) execv(TAR, argv);
1535 exit(1);
1536 }
1537
1538 /* Parent */
1539 *pid = pid1;
1540
1541 (void) close(pipe1[0]); /* close read end */
1542 return (pipe1[1]); /* use write end */
1543 } /* OpenTar */
1544
1545
1546
1547
1548 static int
1549 FTPGetOneTarF(const FTPCIPtr cip, const char *file, const char *const dstdir)
1550 {
1551 char *buf;
1552 size_t bufSize;
1553 int tmpResult;
1554 volatile int result;
1555 int nread, nwrote;
1556 volatile int fd;
1557 volatile int vfd;
1558 const char *volatile vfile;
1559 #ifndef NO_SIGNALS
1560 int sj;
1561 volatile FTPSigProc osigpipe;
1562 volatile FTPCIPtr vcip;
1563 #endif
1564 int pid, status;
1565 char savedCwd[512];
1566 char *volatile basecp;
1567
1568 result = kNoErr;
1569 cip->usingTAR = 0;
1570
1571 if ((file[0] == '\0') || ((file[0] == '/') && (file[1] == '\0'))) {
1572 /* It was "/"
1573 * We can't do that, because "get /.tar"
1574 * or "get .tar" does not work.
1575 */
1576 result = kErrOpenFailed;
1577 cip->errNo = kErrOpenFailed;
1578 return (result);
1579 }
1580
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.
1585 */
1586 result = kErrOpenFailed;
1587 cip->errNo = kErrOpenFailed;
1588 return (result);
1589 }
1590
1591 basecp = strrchr(file, '/');
1592 if (basecp != NULL)
1593 basecp = strrchr(file, '\\');
1594 if (basecp != NULL) {
1595 /* Need to cd to the parent directory and get it
1596 * from there.
1597 */
1598 if (FTPGetCWD(cip, savedCwd, sizeof(savedCwd)) != 0) {
1599 result = kErrOpenFailed;
1600 cip->errNo = kErrOpenFailed;
1601 return (result);
1602 }
1603 result = FTPChdir(cip, file);
1604 if (result != kNoErr) {
1605 return (result);
1606 }
1607 result = FTPChdir(cip, "..");
1608 if (result != kNoErr) {
1609 (void) FTPChdir(cip, savedCwd);
1610 return (result);
1611 }
1612 file = basecp + 1;
1613 }
1614
1615 fd = OpenTar(cip, dstdir, &pid);
1616 if (fd < 0) {
1617 result = kErrOpenFailed;
1618 cip->errNo = kErrOpenFailed;
1619 if (basecp != NULL)
1620 (void) FTPChdir(cip, savedCwd);
1621 return (result);
1622 }
1623
1624 vfd = fd;
1625 vfile = file;
1626
1627 #ifndef NO_SIGNALS
1628 vcip = cip;
1629 osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
1630
1631 gGotBrokenData = 0;
1632 gCanBrokenDataJmp = 0;
1633
1634 #ifdef HAVE_SIGSETJMP
1635 sj = sigsetjmp(gBrokenDataJmp, 1);
1636 #else
1637 sj = setjmp(gBrokenDataJmp);
1638 #endif /* HAVE_SIGSETJMP */
1639
1640 if (sj != 0) {
1641 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
1642 FTPShutdownHost(vcip);
1643
1644 (void) signal(SIGPIPE, SIG_IGN);
1645 (void) close(vfd);
1646 for (;;) {
1647 #ifdef HAVE_WAITPID
1648 if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
1649 break;
1650 #else
1651 if ((wait(&status) < 0) && (errno != EINTR))
1652 break;
1653 #endif
1654 if (WIFEXITED(status) || WIFSIGNALED(status))
1655 break; /* done */
1656 }
1657 if (basecp != NULL)
1658 (void) FTPChdir(cip, savedCwd);
1659 vcip->errNo = kErrRemoteHostClosedConnection;
1660 return(vcip->errNo);
1661 }
1662 gCanBrokenDataJmp = 1;
1663
1664 #endif /* NO_SIGNALS */
1665
1666 tmpResult = FTPStartDataCmd(cip, kNetReading, kTypeBinary, (longest_int) 0, "RETR %s.tar", vfile);
1667
1668 if (tmpResult < 0) {
1669 result = tmpResult;
1670 if (result == kErrGeneric)
1671 result = kErrRETRFailed;
1672 cip->errNo = result;
1673
1674 #ifndef NO_SIGNALS
1675 (void) signal(SIGPIPE, SIG_IGN);
1676 #endif
1677 (void) close(vfd);
1678 for (;;) {
1679 #ifdef HAVE_WAITPID
1680 if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
1681 break;
1682 #else
1683 if ((wait(&status) < 0) && (errno != EINTR))
1684 break;
1685 #endif
1686 if (WIFEXITED(status) || WIFSIGNALED(status))
1687 break; /* done */
1688 }
1689
1690 #ifndef NO_SIGNALS
1691 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
1692 #endif
1693 if (basecp != NULL)
1694 (void) FTPChdir(cip, savedCwd);
1695 return (result);
1696 }
1697
1698 cip->usingTAR = 1;
1699 buf = cip->buf;
1700 bufSize = cip->bufSize;
1701
1702 FTPInitIOTimer(cip);
1703 cip->lname = vfile; /* could be NULL */
1704 cip->rname = vfile;
1705 FTPStartIOTimer(cip);
1706
1707 /* Binary */
1708 for (;;) {
1709 if (! WaitForRemoteInput(cip)) { /* could set cancelXfer */
1710 cip->errNo = result = kErrDataTimedOut;
1711 Error(cip, kDontPerror, "Remote read timed out.\n");
1712 break;
1713 }
1714 if (cip->cancelXfer > 0) {
1715 FTPAbortDataTransfer(cip);
1716 result = cip->errNo = kErrDataTransferAborted;
1717 break;
1718 }
1719 #if !defined(NO_SIGNALS)
1720 gCanBrokenDataJmp = 1;
1721 if (cip->xferTimeout > 0)
1722 (void) alarm(cip->xferTimeout);
1723 #endif /* NO_SIGNALS */
1724 #ifdef 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");
1729 break;
1730 } else if (nread < 0) {
1731 if (errno == EINTR)
1732 continue;
1733 Error(cip, kDoPerror, "Remote read failed.\n");
1734 result = kErrSocketReadFailed;
1735 cip->errNo = kErrSocketReadFailed;
1736 break;
1737 } else if (nread == 0) {
1738 break;
1739 }
1740 #else
1741 nread = read(cip->dataSocket, buf, bufSize);
1742 if (nread < 0) {
1743 if (errno == EINTR)
1744 continue;
1745 Error(cip, kDoPerror, "Remote read failed.\n");
1746 result = kErrSocketReadFailed;
1747 cip->errNo = kErrSocketReadFailed;
1748 break;
1749 } else if (nread == 0) {
1750 break;
1751 }
1752 gCanBrokenDataJmp = 0;
1753 #endif
1754
1755 nwrote = write(fd, buf, nread);
1756 if (nwrote != nread) {
1757 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
1758 result = kErrWriteFailed;
1759 cip->errNo = kErrWriteFailed;
1760 errno = EPIPE;
1761 } else {
1762 Error(cip, kDoPerror, "Local write failed.\n");
1763 result = kErrWriteFailed;
1764 cip->errNo = kErrWriteFailed;
1765 }
1766 break;
1767 }
1768 cip->bytesTransferred += (longest_int) nread;
1769 FTPUpdateIOTimer(cip);
1770 }
1771
1772 #if !defined(NO_SIGNALS)
1773 if (cip->xferTimeout > 0)
1774 (void) alarm(0);
1775 gCanBrokenDataJmp = 0;
1776 #endif /* NO_SIGNALS */
1777
1778 (void) close(fd);
1779 for (;;) {
1780 #ifdef HAVE_WAITPID
1781 if ((waitpid(pid, &status, 0) < 0) && (errno != EINTR))
1782 break;
1783 #else
1784 if ((wait(&status) < 0) && (errno != EINTR))
1785 break;
1786 #endif
1787 if (WIFEXITED(status) || WIFSIGNALED(status))
1788 break; /* done */
1789 }
1790
1791 tmpResult = FTPEndDataCmd(cip, 1);
1792 if ((tmpResult < 0) && (result == 0)) {
1793 result = kErrRETRFailed;
1794 cip->errNo = kErrRETRFailed;
1795 }
1796 FTPStopIOTimer(cip);
1797 #if !defined(NO_SIGNALS)
1798 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
1799 #endif
1800
1801 if ((result == 0) && (cip->bytesTransferred == 0)) {
1802 result = kErrOpenFailed;
1803 cip->errNo = kErrOpenFailed;
1804 }
1805 if (basecp != NULL)
1806 (void) FTPChdir(cip, savedCwd);
1807 return (result);
1808 } /* FTPGetOneTarF */
1809
1810 #endif /* TAR */
1811
1812
1813
1814
1815
1816 static int
1817 FTPGetOneF(
1818 const FTPCIPtr cip,
1819 const char *const file,
1820 const char *dstfile,
1821 int xtype,
1822 const int fdtouse,
1823 longest_int expectedSize,
1824 time_t mdtm,
1825 const int resumeflag,
1826 const int appendflag,
1827 const int deleteflag,
1828 const ConfirmResumeDownloadProc resumeProc)
1829 {
1830 char *buf;
1831 size_t bufSize;
1832 int tmpResult;
1833 volatile int result;
1834 int nread, nwrote;
1835 volatile int fd;
1836 #if ASCII_TRANSLATION
1837 char *src, *srclim;
1838 char *dst, *dstlim;
1839 char outbuf[512];
1840 #endif
1841 volatile longest_int startPoint = 0;
1842 struct utimbuf ut;
1843 struct Stat st;
1844 #if !defined(NO_SIGNALS)
1845 volatile FTPSigProc osigpipe;
1846 volatile FTPCIPtr vcip;
1847 volatile int vfd, vfdtouse;
1848 int sj;
1849 #endif /* NO_SIGNALS */
1850 volatile int created = 0;
1851 int zaction = kConfirmResumeProcSaidBestGuess;
1852 int statrc;
1853 int noMdtmCheck;
1854 time_t now;
1855
1856 if (cip->buf == NULL) {
1857 Error(cip, kDoPerror, "Transfer buffer not allocated.\n");
1858 cip->errNo = kErrNoBuf;
1859 return (cip->errNo);
1860 }
1861
1862 result = kNoErr;
1863 cip->usingTAR = 0;
1864
1865 if (fdtouse < 0) {
1866 /* Only ask for extended information
1867 * if we have the name of the file
1868 * and we didn't already have the
1869 * info.
1870 *
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.
1877 *
1878 */
1879
1880 AutomaticallyUseASCIIModeDependingOnExtension(cip, file, &xtype);
1881 if (expectedSize == kSizeUnknown) {
1882 (void) FTPFileSizeAndModificationTime(cip, file, &expectedSize, xtype, &mdtm);
1883 } else {
1884 (void) FTPFileModificationTime(cip, file, &mdtm);
1885 }
1886
1887 /* For Get, we can't recover very well if it turns out restart
1888 * didn't work, so check beforehand.
1889 */
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
1895 * doing it.
1896 */
1897 SetStartOffset(cip, (longest_int) -1);
1898 }
1899 }
1900 }
1901
1902 if (appendflag == kAppendYes) {
1903 zaction = kConfirmResumeProcSaidAppend;
1904 } else if (cip->hasREST == kCommandNotAvailable) {
1905 zaction = kConfirmResumeProcSaidOverwrite;
1906 } else if (resumeflag == kResumeYes) {
1907 zaction = kConfirmResumeProcSaidBestGuess;
1908 } else {
1909 zaction = kConfirmResumeProcSaidOverwrite;
1910 }
1911
1912 statrc = Stat(dstfile, &st);
1913 if (statrc == 0) {
1914 if (resumeProc != NULL) {
1915 zaction = (*resumeProc)(
1916 &dstfile,
1917 (longest_int) st.st_size,
1918 st.st_mtime,
1919 file,
1920 expectedSize,
1921 mdtm,
1922 &startPoint
1923 );
1924 }
1925
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.
1930 *
1931 * Try and decide if we need to get
1932 * the entire file, or just part of it.
1933 */
1934
1935 startPoint = (longest_int) st.st_size;
1936 zaction = kConfirmResumeProcSaidResume;
1937
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).
1945 */
1946 noMdtmCheck = 0;
1947 if (mdtm != kModTimeUnknown) {
1948 time(&now);
1949 if ((st.st_mtime > now) || (((now - st.st_mtime) < 46200) && ((now - mdtm) >= 46200)))
1950 noMdtmCheck = 1;
1951 }
1952
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.
1966 */
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!
1972 */
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.
1984 */
1985 zaction = kConfirmResumeProcSaidOverwrite;
1986 } else {
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.
1991 */
1992 }
1993 } else if (mdtm < st.st_mtime) {
1994 /* Remote file is older than
1995 * local file. Don't overwrite
1996 * our file.
1997 */
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.
2005 */
2006 zaction = kConfirmResumeProcSaidOverwrite;
2007 }
2008 } else {
2009 zaction = kConfirmResumeProcSaidOverwrite;
2010 }
2011 }
2012 } else {
2013 zaction = kConfirmResumeProcSaidOverwrite;
2014 }
2015
2016 if (zaction == kConfirmResumeProcSaidCancel) {
2017 /* User wants to cancel this file and any
2018 * remaining in batch.
2019 */
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);
2026 return (kNoErr);
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);
2034 return (kNoErr);
2035 } else if (startPoint > expectedSize) {
2036 /* Cannot set start point past end of remote file */
2037 cip->errNo = result = kErrSetStartPoint;
2038 return (result);
2039 }
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) */ {
2046 created = 1;
2047 startPoint = (longest_int) 0;
2048 fd = Open(dstfile, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 00666);
2049 }
2050
2051 if (fd < 0) {
2052 Error(cip, kDoPerror, "Cannot open local file %s for writing.\n", dstfile);
2053 result = kErrOpenFailed;
2054 cip->errNo = kErrOpenFailed;
2055 return (result);
2056 }
2057
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);
2063 #endif
2064 (void) close(fd);
2065 if (mdtm != kModTimeUnknown) {
2066 cip->mdtm = mdtm;
2067 (void) time(&ut.actime);
2068 ut.modtime = mdtm;
2069 (void) utime(dstfile, &ut);
2070 }
2071 if (deleteflag == kDeleteYes)
2072 (void) FTPDelete(cip, file, kRecursiveNo, kGlobNo);
2073 return (kNoErr);
2074 }
2075 } else {
2076 fd = fdtouse;
2077 }
2078
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.
2083 *
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.
2087 *
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.
2092 */
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);
2101 }
2102
2103 #ifdef NO_SIGNALS
2104 #else /* NO_SIGNALS */
2105 vcip = cip;
2106 vfdtouse = fdtouse;
2107 vfd = fd;
2108 osigpipe = (volatile FTPSigProc) signal(SIGPIPE, BrokenData);
2109
2110 gGotBrokenData = 0;
2111 gCanBrokenDataJmp = 0;
2112
2113 #ifdef HAVE_SIGSETJMP
2114 sj = sigsetjmp(gBrokenDataJmp, 1);
2115 #else
2116 sj = setjmp(gBrokenDataJmp);
2117 #endif /* HAVE_SIGSETJMP */
2118
2119 if (sj != 0) {
2120 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
2121 if (vfdtouse < 0) {
2122 (void) close(vfd);
2123 }
2124 FTPShutdownHost(vcip);
2125 vcip->errNo = kErrRemoteHostClosedConnection;
2126 return(vcip->errNo);
2127 }
2128 gCanBrokenDataJmp = 1;
2129 #endif /* NO_SIGNALS */
2130
2131 tmpResult = FTPStartDataCmd(cip, kNetReading, xtype, startPoint, "RETR %s", file);
2132
2133 if (tmpResult < 0) {
2134 result = tmpResult;
2135 if (result == kErrGeneric)
2136 result = kErrRETRFailed;
2137 cip->errNo = result;
2138 if (fdtouse < 0) {
2139 (void) close(fd);
2140 if ((created != 0) && (appendflag == kAppendNo) && (cip->startPoint == 0))
2141 (void) unlink(dstfile);
2142 }
2143 #if !defined(NO_SIGNALS)
2144 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
2145 #endif /* NO_SIGNALS */
2146 return (result);
2147 }
2148
2149 if ((startPoint != 0) && (cip->startPoint == 0)) {
2150 /* Remote could not or would not set the start offset
2151 * to what we wanted.
2152 *
2153 * So now we have to undo our seek.
2154 */
2155 if (Lseek(fd, (off_t) 0, SEEK_SET) != (off_t) 0) {
2156 cip->errNo = kErrLseekFailed;
2157 if (fdtouse < 0) {
2158 (void) close(fd);
2159 }
2160 #if !defined(NO_SIGNALS)
2161 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
2162 #endif /* NO_SIGNALS */
2163 return (cip->errNo);
2164 }
2165 startPoint = 0;
2166 }
2167
2168 buf = cip->buf;
2169 bufSize = cip->bufSize;
2170
2171 FTPInitIOTimer(cip);
2172 cip->mdtm = mdtm;
2173 (void) time(&ut.actime);
2174 ut.modtime = mdtm;
2175 cip->expectedSize = expectedSize;
2176 cip->lname = dstfile; /* could be NULL */
2177 cip->rname = file;
2178 if (fdtouse >= 0)
2179 cip->useProgressMeter = 0;
2180 FTPStartIOTimer(cip);
2181
2182 #if ASCII_TRANSLATION
2183 if (xtype == kTypeAscii) {
2184 /* Ascii */
2185 for (;;) {
2186 if (! WaitForRemoteInput(cip)) { /* could set cancelXfer */
2187 cip->errNo = result = kErrDataTimedOut;
2188 Error(cip, kDontPerror, "Remote read timed out.\n");
2189 break;
2190 }
2191 if (cip->cancelXfer > 0) {
2192 FTPAbortDataTransfer(cip);
2193 result = cip->errNo = kErrDataTransferAborted;
2194 break;
2195 }
2196 #ifdef TESTING_ABOR
2197 if (cip->bytesTransferred > 0) {
2198 cip->cancelXfer = 1;
2199 FTPAbortDataTransfer(cip);
2200 result = cip->errNo = kErrDataTransferAborted;
2201 break;
2202 }
2203 #endif /* TESTING_ABOR */
2204 #ifdef NO_SIGNALS
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");
2209 break;
2210 } else if (nread < 0) {
2211 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2212 result = cip->errNo = kErrSocketReadFailed;
2213 errno = EPIPE;
2214 Error(cip, kDoPerror, "Lost data connection to remote host.\n");
2215 } else if (errno == EINTR) {
2216 continue;
2217 } else {
2218 Error(cip, kDoPerror, "Remote read failed.\n");
2219 result = kErrSocketReadFailed;
2220 cip->errNo = kErrSocketReadFailed;
2221 }
2222 break;
2223 } else if (nread == 0) {
2224 break;
2225 }
2226 #else
2227 gCanBrokenDataJmp = 1;
2228 if (cip->xferTimeout > 0)
2229 (void) alarm(cip->xferTimeout);
2230 nread = read(cip->dataSocket, buf, bufSize);
2231 if (nread < 0) {
2232 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2233 result = cip->errNo = kErrSocketReadFailed;
2234 errno = EPIPE;
2235 Error(cip, kDoPerror, "Lost data connection to remote host.\n");
2236 (void) shutdown(cip->dataSocket, 2);
2237 } else if (errno == EINTR) {
2238 continue;
2239 } else {
2240 result = cip->errNo = kErrSocketReadFailed;
2241 Error(cip, kDoPerror, "Remote read failed.\n");
2242 (void) shutdown(cip->dataSocket, 2);
2243 }
2244 break;
2245 } else if (nread == 0) {
2246 break;
2247 }
2248
2249 gCanBrokenDataJmp = 0;
2250 #endif /* NO_SIGNALS */
2251
2252 src = buf;
2253 srclim = src + nread;
2254 dst = outbuf;
2255 dstlim = dst + sizeof(outbuf);
2256 while (src < srclim) {
2257 if (*src == '\r') {
2258 src++;
2259 continue;
2260 }
2261 if (dst >= dstlim) {
2262 nwrote = write(fd, outbuf, (size_t) (dst - outbuf));
2263 if (nwrote == (int) (dst - outbuf)) {
2264 /* Success. */
2265 dst = outbuf;
2266 } else if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2267 result = kErrWriteFailed;
2268 cip->errNo = kErrWriteFailed;
2269 errno = EPIPE;
2270 (void) shutdown(cip->dataSocket, 2);
2271 goto brk;
2272 } else {
2273 Error(cip, kDoPerror, "Local write failed.\n");
2274 result = kErrWriteFailed;
2275 cip->errNo = kErrWriteFailed;
2276 (void) shutdown(cip->dataSocket, 2);
2277 goto brk;
2278 }
2279 }
2280 *dst++ = *src++;
2281 }
2282 if (dst > outbuf) {
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;
2288 errno = EPIPE;
2289 (void) shutdown(cip->dataSocket, 2);
2290 goto brk;
2291 } else {
2292 Error(cip, kDoPerror, "Local write failed.\n");
2293 result = kErrWriteFailed;
2294 cip->errNo = kErrWriteFailed;
2295 (void) shutdown(cip->dataSocket, 2);
2296 goto brk;
2297 }
2298 }
2299 }
2300
2301 if (mdtm != kModTimeUnknown) {
2302 (void) utime(dstfile, &ut);
2303 }
2304 cip->bytesTransferred += (longest_int) nread;
2305 FTPUpdateIOTimer(cip);
2306 }
2307 } else
2308 #endif /* ASCII_TRANSLATION */
2309 {
2310 /* Binary */
2311 for (;;) {
2312 if (! WaitForRemoteInput(cip)) { /* could set cancelXfer */
2313 cip->errNo = result = kErrDataTimedOut;
2314 Error(cip, kDontPerror, "Remote read timed out.\n");
2315 break;
2316 }
2317 if (cip->cancelXfer > 0) {
2318 FTPAbortDataTransfer(cip);
2319 result = cip->errNo = kErrDataTransferAborted;
2320 break;
2321 }
2322 #ifdef TESTING_ABOR
2323 if (cip->bytesTransferred > 0) {
2324 cip->cancelXfer = 1;
2325 FTPAbortDataTransfer(cip);
2326 result = cip->errNo = kErrDataTransferAborted;
2327 break;
2328 }
2329 #endif /* TESTING_ABOR */
2330 #ifdef NO_SIGNALS
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");
2335 break;
2336 } else if (nread < 0) {
2337 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2338 result = cip->errNo = kErrSocketReadFailed;
2339 errno = EPIPE;
2340 Error(cip, kDoPerror, "Lost data connection to remote host.\n");
2341 } else if (errno == EINTR) {
2342 continue;
2343 } else {
2344 Error(cip, kDoPerror, "Remote read failed.\n");
2345 result = kErrSocketReadFailed;
2346 cip->errNo = kErrSocketReadFailed;
2347 }
2348 break;
2349 } else if (nread == 0) {
2350 break;
2351 }
2352 #else
2353 gCanBrokenDataJmp = 1;
2354 if (cip->xferTimeout > 0)
2355 (void) alarm(cip->xferTimeout);
2356 nread = read(cip->dataSocket, buf, bufSize);
2357 if (nread < 0) {
2358 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2359 result = cip->errNo = kErrSocketReadFailed;
2360 errno = EPIPE;
2361 Error(cip, kDoPerror, "Lost data connection to remote host.\n");
2362 } else if (errno == EINTR) {
2363 continue;
2364 } else {
2365 result = cip->errNo = kErrSocketReadFailed;
2366 Error(cip, kDoPerror, "Remote read failed.\n");
2367 }
2368 (void) shutdown(cip->dataSocket, 2);
2369 break;
2370 } else if (nread == 0) {
2371 break;
2372 }
2373 gCanBrokenDataJmp = 0;
2374 #endif /* NO_SIGNALS */
2375
2376 nwrote = write(fd, buf, nread);
2377 if (nwrote != nread) {
2378 if ((gGotBrokenData != 0) || (errno == EPIPE)) {
2379 result = kErrWriteFailed;
2380 cip->errNo = kErrWriteFailed;
2381 errno = EPIPE;
2382 } else {
2383 Error(cip, kDoPerror, "Local write failed.\n");
2384 result = kErrWriteFailed;
2385 cip->errNo = kErrWriteFailed;
2386 }
2387 (void) shutdown(cip->dataSocket, 2);
2388 break;
2389 }
2390
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.
2394 */
2395 if (mdtm != kModTimeUnknown) {
2396 (void) utime(dstfile, &ut);
2397 }
2398 cip->bytesTransferred += (longest_int) nread;
2399 FTPUpdateIOTimer(cip);
2400 }
2401 }
2402
2403 #if ASCII_TRANSLATION
2404 brk:
2405 #endif
2406
2407 #if !defined(NO_SIGNALS)
2408 if (cip->xferTimeout > 0)
2409 (void) alarm(0);
2410 gCanBrokenDataJmp = 0;
2411 #endif /* NO_SIGNALS */
2412
2413 if (fdtouse < 0) {
2414 /* If they gave us a descriptor (fdtouse >= 0),
2415 * leave it open, otherwise we opened it, so
2416 * we need to close it.
2417 */
2418 (void) close(fd);
2419 fd = -1;
2420 }
2421
2422 tmpResult = FTPEndDataCmd(cip, 1);
2423 if ((tmpResult < 0) && (result == 0)) {
2424 result = kErrRETRFailed;
2425 cip->errNo = kErrRETRFailed;
2426 }
2427 FTPStopIOTimer(cip);
2428 #if !defined(NO_SIGNALS)
2429 (void) signal(SIGPIPE, (FTPSigProc) osigpipe);
2430 #endif /* NO_SIGNALS */
2431
2432 if ((mdtm != kModTimeUnknown) && (cip->bytesTransferred > 0)) {
2433 (void) utime(dstfile, &ut);
2434 }
2435
2436 if (result == kNoErr) {
2437 cip->numDownloads++;
2438
2439 if (deleteflag == kDeleteYes) {
2440 result = FTPDelete(cip, file, kRecursiveNo, kGlobNo);
2441 }
2442 }
2443
2444 return (result);
2445 } /* FTPGetOneF */
2446
2447
2448
2449
2450 int
2451 FTPGetOneFile3(
2452 const FTPCIPtr cip,
2453 const char *const file,
2454 const char *const dstfile,
2455 const int xtype,
2456 const int fdtouse,
2457 const int resumeflag,
2458 const int appendflag,
2459 const int deleteflag,
2460 const ConfirmResumeDownloadProc resumeProc,
2461 int UNUSED(reserved))
2462 {
2463 int result;
2464
2465 LIBNCFTP_USE_VAR(reserved);
2466 if (cip == NULL)
2467 return (kErrBadParameter);
2468 if (strcmp(cip->magic, kLibraryMagic))
2469 return (kErrBadMagic);
2470
2471 if ((file == NULL) || (file[0] == '\0'))
2472 return (kErrBadParameter);
2473 if (fdtouse < 0) {
2474 if ((dstfile == NULL) || (dstfile[0] == '\0'))
2475 return (kErrBadParameter);
2476 }
2477
2478 result = FTPGetOneF(cip, file, dstfile, xtype, fdtouse, kSizeUnknown, kModTimeUnknown, resumeflag, appendflag, deleteflag, resumeProc);
2479 return (result);
2480 } /* FTPGetOneFile3 */
2481
2482
2483
2484
2485 int
2486 FTPGetFiles3(
2487 const FTPCIPtr cip,
2488 const char *pattern1,
2489 const char *const dstdir1,
2490 const int recurse,
2491 int doGlob,
2492 const int xtype,
2493 const int resumeflag,
2494 int appendflag,
2495 const int deleteflag,
2496 const int tarflag,
2497 const ConfirmResumeDownloadProc resumeProc,
2498 int UNUSED(reserved))
2499 {
2500 LineList globList;
2501 LinePtr itemPtr;
2502 FileInfoList files;
2503 FileInfoPtr filePtr;
2504 int batchResult;
2505 int result;
2506 char *ldir;
2507 char *cp;
2508 const char *dstdir;
2509 const char *pattern;
2510 char *pattern2, *dstdir2;
2511 char c;
2512 int recurse1;
2513 int errRc;
2514
2515 LIBNCFTP_USE_VAR(reserved);
2516 if (cip == NULL)
2517 return (kErrBadParameter);
2518 if (strcmp(cip->magic, kLibraryMagic))
2519 return (kErrBadMagic);
2520 if (pattern1 == NULL)
2521 return (kErrBadParameter);
2522
2523 dstdir2 = NULL;
2524 pattern2 = NULL;
2525
2526 if (dstdir1 == NULL) {
2527 dstdir = NULL;
2528 } else {
2529 dstdir2 = StrDup(dstdir1);
2530 if (dstdir2 == NULL) {
2531 errRc = kErrMallocFailed;
2532 goto return_err;
2533 }
2534 StrRemoveTrailingLocalPathDelim(dstdir2);
2535 dstdir = dstdir2;
2536 }
2537
2538 pattern2 = StrDup(pattern1);
2539 if (pattern2 == NULL) {
2540 errRc = kErrMallocFailed;
2541 goto return_err;
2542 }
2543 StrRemoveTrailingSlashes(pattern2);
2544 pattern = pattern2;
2545
2546 if (pattern[0] == '\0') {
2547 if (recurse == kRecursiveNo) {
2548 errRc = kErrBadParameter;
2549 goto return_err;
2550 }
2551 pattern = ".";
2552 doGlob = kGlobNo;
2553 } else if (strcmp(pattern, ".") == 0) {
2554 if (recurse == kRecursiveNo) {
2555 errRc = kErrBadParameter;
2556 goto return_err;
2557 }
2558 doGlob = kGlobNo;
2559 }
2560 if (recurse == kRecursiveYes)
2561 appendflag = kAppendNo;
2562
2563 batchResult = FTPRemoteGlob(cip, &globList, pattern, doGlob);
2564 if (batchResult != kNoErr) {
2565 errRc = batchResult;
2566 goto return_err;
2567 }
2568
2569 cip->cancelXfer = 0; /* should already be zero */
2570
2571 for (itemPtr = globList.first; itemPtr != NULL; itemPtr = itemPtr->next) {
2572 if ((recurse == kRecursiveYes) && (FTPIsDir(cip, itemPtr->line) > 0)) {
2573 #ifdef TAR
2574 if ((tarflag == kTarYes) && (xtype == kTypeBinary) && (appendflag == kAppendNo) && (deleteflag == kDeleteNo) && (FTPGetOneTarF(cip, itemPtr->line, dstdir) == kNoErr)) {
2575 /* Great! */
2576 continue;
2577 }
2578 #endif /* TAR */
2579 (void) FTPRemoteRecursiveFileList1(cip, itemPtr->line, &files);
2580 (void) ComputeLNames(&files, itemPtr->line, dstdir, 1);
2581 recurse1 = recurse;
2582 } else {
2583 recurse1 = kRecursiveNo;
2584 (void) LineToFileInfoList(itemPtr, &files);
2585 (void) ComputeRNames(&files, ".", 0, 1);
2586 (void) ComputeLNames(&files, NULL, dstdir, 0);
2587 }
2588 if (cip->cancelXfer > 0) {
2589 DisposeFileInfoListContents(&files);
2590 break;
2591 }
2592
2593 #if 0
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",
2596 filePtr->rname,
2597 filePtr->lname,
2598 filePtr->rlinkto ? filePtr->rlinkto : "",
2599 filePtr->size,
2600 (unsigned int) filePtr->mdtm,
2601 filePtr->type
2602 );
2603 }
2604 #endif
2605
2606
2607 for (filePtr = files.first; filePtr != NULL; filePtr = filePtr->next) {
2608 if (cip->connected == 0) {
2609 if (batchResult == kNoErr)
2610 batchResult = kErrRemoteHostClosedConnection;
2611 break;
2612 }
2613 if (filePtr->type == 'd') {
2614 #if defined(WIN32) || defined(_WINDOWS)
2615 (void) MkDirs(filePtr->lname, 00777);
2616 #else
2617 (void) mkdir(filePtr->lname, 00777);
2618 #endif
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;
2626 } else {
2627 if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
2628 batchResult = result;
2629 }
2630 if (result == kErrUserCanceled)
2631 cip->cancelXfer = 1;
2632 if (cip->cancelXfer > 0)
2633 break;
2634 } else {
2635 ldir = filePtr->lname;
2636 cp = StrRFindLocalPathDelim(ldir);
2637 if (cp != NULL) {
2638 while (cp > ldir) {
2639 if (! IsLocalPathDelim(*cp)) {
2640 ++cp;
2641 break;
2642 }
2643 --cp;
2644 }
2645 if (cp > ldir) {
2646 c = *cp;
2647 *cp = '\0';
2648 if (MkDirs(ldir, 00777) < 0) {
2649 Error(cip, kDoPerror, "Could not create local directory \"%s\"\n", ldir);
2650 batchResult = kErrGeneric;
2651 *cp = c;
2652 continue;
2653 }
2654 *cp = c;
2655 }
2656 }
2657 result = FTPGetOneF(cip, filePtr->rname, filePtr->lname, xtype, -1, filePtr->size, filePtr->mdtm, resumeflag, appendflag, deleteflag, resumeProc);
2658
2659 if (files.nFileInfos == 1) {
2660 if (result != kNoErr)
2661 batchResult = result;
2662 } else {
2663 if ((result != kNoErr) && (result != kErrLocalFileNewer) && (result != kErrRemoteFileNewer) && (result != kErrLocalSameAsRemote))
2664 batchResult = result;
2665 }
2666 if (result == kErrUserCanceled)
2667 cip->cancelXfer = 1;
2668 if (cip->cancelXfer > 0)
2669 break;
2670 }
2671 }
2672 if (cip->cancelXfer > 0) {
2673 DisposeFileInfoListContents(&files);
2674 break;
2675 }
2676
2677 #ifdef HAVE_SYMLINK
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 */
2684 }
2685 }
2686 }
2687 #endif /* HAVE_SYMLINK */
2688
2689
2690 DisposeFileInfoListContents(&files);
2691 }
2692
2693 DisposeLineListContents(&globList);
2694 if (batchResult < 0)
2695 cip->errNo = batchResult;
2696 errRc = batchResult;
2697
2698 return_err:
2699 if (dstdir2 != NULL)
2700 free(dstdir2);
2701 if (pattern2 != NULL)
2702 free(pattern2);
2703 return (errRc);
2704 } /* FTPGetFiles3 */
2705
2706
2707
2708
2709 /*------------------------- wrappers for old routines ----------------------*/
2710
2711 int
2712 FTPGetOneFile(const FTPCIPtr cip, const char *const file, const char *const dstfile)
2713 {
2714 return (FTPGetOneFile3(cip, file, dstfile, kTypeBinary, -1, kResumeNo, kAppendNo, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
2715 } /* FTPGetOneFile */
2716
2717
2718
2719
2720 int
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)
2722 {
2723 return (FTPGetOneFile3(cip, file, dstfile, xtype, fdtouse, resumeflag, appendflag, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
2724 } /* FTPGetOneFile2 */
2725
2726
2727
2728
2729 int
2730 FTPGetFiles(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
2731 {
2732 return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeBinary, kResumeNo, kAppendNo, kDeleteNo, kTarYes, (ConfirmResumeDownloadProc) 0, 0));
2733 } /* FTPGetFiles */
2734
2735
2736
2737
2738 int
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)
2740 {
2741 return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, xtype, resumeflag, appendflag, kDeleteNo, kTarYes, (ConfirmResumeDownloadProc) 0, 0));
2742 } /* FTPGetFiles2 */
2743
2744
2745
2746
2747 int
2748 FTPGetOneFileAscii(const FTPCIPtr cip, const char *const file, const char *const dstfile)
2749 {
2750 return (FTPGetOneFile3(cip, file, dstfile, kTypeAscii, -1, kResumeNo, kAppendNo, kDeleteNo, (ConfirmResumeDownloadProc) 0, 0));
2751 } /* FTPGetOneFileAscii */
2752
2753
2754
2755
2756 int
2757 FTPGetFilesAscii(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
2758 {
2759 return (FTPGetFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeAscii, kResumeNo, kAppendNo, kDeleteNo, kTarNo, (ConfirmResumeDownloadProc) 0, 0));
2760 } /* FTPGetFilesAscii */
2761
2762
2763
2764
2765 int
2766 FTPPutOneFile(const FTPCIPtr cip, const char *const file, const char *const dstfile)
2767 {
2768 return (FTPPutOneFile3(cip, file, dstfile, kTypeBinary, -1, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2769 } /* FTPPutOneFile */
2770
2771
2772
2773
2774 int
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)
2776 {
2777 return (FTPPutOneFile3(cip, file, dstfile, xtype, fdtouse, appendflag, tmppfx, tmpsfx, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2778 } /* FTPPutOneFile2 */
2779
2780
2781
2782
2783 int
2784 FTPPutFiles(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
2785 {
2786 return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeBinary, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2787 } /* FTPPutFiles */
2788
2789
2790
2791
2792 int
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)
2794 {
2795 return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, xtype, appendflag, tmppfx, tmpsfx, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2796 } /* FTPPutFiles2 */
2797
2798
2799
2800
2801 int
2802 FTPPutOneFileAscii(const FTPCIPtr cip, const char *const file, const char *const dstfile)
2803 {
2804 return (FTPPutOneFile3(cip, file, dstfile, kTypeAscii, -1, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2805 } /* FTPPutOneFileAscii */
2806
2807
2808
2809
2810 int
2811 FTPPutFilesAscii(const FTPCIPtr cip, const char *const pattern, const char *const dstdir, const int recurse, const int doGlob)
2812 {
2813 return (FTPPutFiles3(cip, pattern, dstdir, recurse, doGlob, kTypeAscii, 0, NULL, NULL, kResumeNo, kDeleteNo, NoConfirmResumeUploadProc, 0));
2814 } /* FTPPutFilesAscii */
2815
2816
2817
2818 int
2819 FTPListToMemory(const FTPCIPtr cip, const char *const pattern, const LineListPtr llines, const char *const lsflags)
2820 {
2821 return (FTPListToMemory2(cip, pattern, llines, lsflags, 1, (int *) 0));
2822 } /* FTPListToMemory */
2823
2824 /* eof IO.c */