static CURLcode ftp_done()

in libs/curl/lib/ftp.c [3351:3572]


static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
                         bool premature)
{
  struct connectdata *conn = data->conn;
  struct FTP *ftp = data->req.p.ftp;
  struct ftp_conn *ftpc = &conn->proto.ftpc;
  struct pingpong *pp = &ftpc->pp;
  ssize_t nread;
  int ftpcode;
  CURLcode result = CURLE_OK;
  char *rawPath = NULL;
  size_t pathLen = 0;

  if(!ftp)
    return CURLE_OK;

  switch(status) {
  case CURLE_BAD_DOWNLOAD_RESUME:
  case CURLE_FTP_WEIRD_PASV_REPLY:
  case CURLE_FTP_PORT_FAILED:
  case CURLE_FTP_ACCEPT_FAILED:
  case CURLE_FTP_ACCEPT_TIMEOUT:
  case CURLE_FTP_COULDNT_SET_TYPE:
  case CURLE_FTP_COULDNT_RETR_FILE:
  case CURLE_PARTIAL_FILE:
  case CURLE_UPLOAD_FAILED:
  case CURLE_REMOTE_ACCESS_DENIED:
  case CURLE_FILESIZE_EXCEEDED:
  case CURLE_REMOTE_FILE_NOT_FOUND:
  case CURLE_WRITE_ERROR:
    /* the connection stays alive fine even though this happened */
  case CURLE_OK: /* does not affect the control connection's status */
    if(!premature)
      break;

    /* until we cope better with prematurely ended requests, let them
     * fallback as if in complete failure */
    FALLTHROUGH();
  default:       /* by default, an error means the control connection is
                    wedged and should not be used anymore */
    ftpc->ctl_valid = FALSE;
    ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
                             current path, as this connection is going */
    connclose(conn, "FTP ended with bad error code");
    result = status;      /* use the already set error code */
    break;
  }

  if(data->state.wildcardmatch) {
    if(data->set.chunk_end && ftpc->file) {
      Curl_set_in_callback(data, true);
      data->set.chunk_end(data->set.wildcardptr);
      Curl_set_in_callback(data, false);
    }
    ftpc->known_filesize = -1;
  }

  if(!result)
    /* get the url-decoded "raw" path */
    result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen,
                            REJECT_CTRL);
  if(result) {
    /* We can limp along anyway (and should try to since we may already be in
     * the error path) */
    ftpc->ctl_valid = FALSE; /* mark control connection as bad */
    connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
    free(ftpc->prevpath);
    ftpc->prevpath = NULL; /* no path remembering */
  }
  else { /* remember working directory for connection reuse */
    if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
      free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
    else {
      free(ftpc->prevpath);

      if(!ftpc->cwdfail) {
        if(data->set.ftp_filemethod == FTPFILE_NOCWD)
          pathLen = 0; /* relative path => working directory is FTP home */
        else
          pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */

        rawPath[pathLen] = '\0';
        ftpc->prevpath = rawPath;
      }
      else {
        free(rawPath);
        ftpc->prevpath = NULL; /* no path */
      }
    }

    if(ftpc->prevpath)
      infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath);
  }

  /* free the dir tree and file parts */
  freedirs(ftpc);

  /* shut down the socket to inform the server we are done */

#ifdef _WIN32_WCE
  shutdown(conn->sock[SECONDARYSOCKET], 2);  /* SD_BOTH */
#endif

  if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
    if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
      /* partial download completed */
      result = Curl_pp_sendf(data, pp, "%s", "ABOR");
      if(result) {
        failf(data, "Failure sending ABOR command: %s",
              curl_easy_strerror(result));
        ftpc->ctl_valid = FALSE; /* mark control connection as bad */
        connclose(conn, "ABOR command failed"); /* connection closure */
      }
    }

    close_secondarysocket(data);
  }

  if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
     pp->pending_resp && !premature) {
    /*
     * Let's see what the server says about the transfer we just performed,
     * but lower the timeout as sometimes this connection has died while the
     * data has been transferred. This happens when doing through NATs etc that
     * abandon old silent connections.
     */
    timediff_t old_time = pp->response_time;

    pp->response_time = 60*1000; /* give it only a minute for now */
    pp->response = Curl_now(); /* timeout relative now */

    result = Curl_GetFTPResponse(data, &nread, &ftpcode);

    pp->response_time = old_time; /* set this back to previous value */

    if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
      failf(data, "control connection looks dead");
      ftpc->ctl_valid = FALSE; /* mark control connection as bad */
      connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
    }

    if(result) {
      Curl_safefree(ftp->pathalloc);
      return result;
    }

    if(ftpc->dont_check && data->req.maxdownload > 0) {
      /* we have just sent ABOR and there is no reliable way to check if it was
       * successful or not; we have to close the connection now */
      infof(data, "partial download completed, closing connection");
      connclose(conn, "Partial download with no ability to check");
      return result;
    }

    if(!ftpc->dont_check) {
      /* 226 Transfer complete, 250 Requested file action okay, completed. */
      switch(ftpcode) {
      case 226:
      case 250:
        break;
      case 552:
        failf(data, "Exceeded storage allocation");
        result = CURLE_REMOTE_DISK_FULL;
        break;
      default:
        failf(data, "server did not report OK, got %d", ftpcode);
        result = CURLE_PARTIAL_FILE;
        break;
      }
    }
  }

  if(result || premature)
    /* the response code from the transfer showed an error already so no
       use checking further */
    ;
  else if(data->state.upload) {
    if((-1 != data->state.infilesize) &&
       (data->state.infilesize != data->req.writebytecount) &&
       !data->set.crlf &&
       (ftp->transfer == PPTRANSFER_BODY)) {
      failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
            " out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
            data->req.writebytecount, data->state.infilesize);
      result = CURLE_PARTIAL_FILE;
    }
  }
  else {
    if((-1 != data->req.size) &&
       (data->req.size != data->req.bytecount) &&
#ifdef CURL_DO_LINEEND_CONV
       /* Most FTP servers do not adjust their file SIZE response for CRLFs,
        * so we will check to see if the discrepancy can be explained by the
        * number of CRLFs we have changed to LFs.
        */
       ((data->req.size + data->state.crlf_conversions) !=
        data->req.bytecount) &&
#endif /* CURL_DO_LINEEND_CONV */
       (data->req.maxdownload != data->req.bytecount)) {
      failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
            " bytes", data->req.bytecount);
      result = CURLE_PARTIAL_FILE;
    }
    else if(!ftpc->dont_check &&
            !data->req.bytecount &&
            (data->req.size>0)) {
      failf(data, "No data was received");
      result = CURLE_FTP_COULDNT_RETR_FILE;
    }
  }

  /* clear these for next connection */
  ftp->transfer = PPTRANSFER_BODY;
  ftpc->dont_check = FALSE;

  /* Send any post-transfer QUOTE strings? */
  if(!status && !result && !premature && data->set.postquote)
    result = ftp_sendquote(data, conn, data->set.postquote);
  CURL_TRC_FTP(data, "[%s] done, result=%d", FTP_DSTATE(data), result);
  Curl_safefree(ftp->pathalloc);
  return result;
}