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 */ 2825