/****************************************************************************** libprozilla - a download accelerator library Copyright (C) 2001 Kalum Somaratna This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ******************************************************************************/ /* FTP support. */ /* $Id$ */ #include "common.h" #include "prozilla.h" #include "connect.h" #include "misc.h" #include "url.h" #include "netrc.h" #include "debug.h" #include "ftpparse.h" #include "ftp.h" /* #define UNIMPLEMENTED_CMD(a) ((a == 500) || (a == 502) || (a == 504)) */ #define BUFFER_SIZE 2048 /****************************************************************************** Return the numeric response of the FTP server by reading the first three characters in the buffer. ******************************************************************************/ static int ftp_get_return(const char *ftp_buffer) { char code[4]; strncpy(code, ftp_buffer, 3); code[3] = '\0'; return atoi(code); } /****************************************************************************** ... ******************************************************************************/ static uerr_t ftp_get_reply(connection_t * connection) { int cont = 0; int code; response_line *srl; /* FIXME Make the line variable dynamically allocated. */ /* Allocate the space in the buffer for the request. */ char szBuffer[FTP_BUFFER_SIZE]; char *strtok_saveptr; // = (char *) alloca(FTP_BUFFER_SIZE); memset(szBuffer, 0, FTP_BUFFER_SIZE); if (ftp_get_line(connection, szBuffer) != FTPOK) return FTPERR; if (!isdigit(*szBuffer)) return FTPERR; if (*szBuffer == '\0') return FTPERR; code = ftp_get_return(szBuffer); if (szBuffer[3] == '-') cont = 1; else cont = 0; (void)strtok_r(szBuffer, "\r\n", &strtok_saveptr); srl = connection->serv_ret_lines = kmalloc(sizeof(response_line)); srl->line = kstrdup(szBuffer); srl->next = 0; /* Add the first line to the struct. */ while (cont) { if (ftp_get_line(connection, szBuffer) != FTPOK) return FTPERR; /* Server closed the connection. */ if (*szBuffer == '\0') return FTPERR; // proz_debug("Code %d",code); if ((ftp_get_return(szBuffer) == code) && (szBuffer[3] == ' ')) cont = 0; (void)strtok_r(szBuffer, "\r\n", &strtok_saveptr); // proz_debug(_("Message = %s"), szBuffer); srl->next = kmalloc(sizeof(response_line)); srl = srl->next; srl->line = kstrdup(szBuffer); srl->next = 0; } return FTPOK; } /****************************************************************************** ... ******************************************************************************/ int ftp_check_msg(connection_t * connection, int len) { int ret; if ((ret = krecv(connection->ctrl_sock, connection->szBuffer, len, MSG_PEEK, &connection->ctrl_timeout)) == -1) { proz_debug(_("Error checking for FTP data: %s"), strerror(errno)); return ret; } return ret; } /****************************************************************************** ... ******************************************************************************/ int ftp_read_msg(connection_t * connection, int len) { int ret; if ((ret = krecv(connection->ctrl_sock, connection->szBuffer, len, 0, &connection->ctrl_timeout)) == -1) { proz_debug(_("Error receiving FTP data: %s"), strerror(errno)); return ret; } return ret; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_send_msg(connection_t * connection, const char *format, ...) { longstring command; va_list args; va_start(args, format); #ifdef HAVE_VSNPRINTF vsnprintf(command, sizeof(command) - 1, format, args); command[sizeof(command) - 1] = '\0'; #else vsprintf(command, format, args); #endif va_end(args); proz_debug(_("Sending: %s"), command); if ((ksend(connection->ctrl_sock, command, strlen(command), 0, &connection->ctrl_timeout)) == -1) { proz_debug(_("Error sending FTP data: %s"), strerror(errno)); return WRITEERR; } return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_get_line(connection_t * connection, char *line) { int iLen, iBuffLen = 0, ret = 0; char *szptr = line, ch; connection->szBuffer = &ch; while ((iBuffLen < BUFFER_SIZE) && ((ret = ftp_check_msg(connection, 1)) > 0)) { /* Now get the full string. */ iLen = ftp_read_msg(connection, 1); if (iLen != 1) return FTPERR; iBuffLen += iLen; *szptr = ch; szptr += iLen; if (ch == '\n') break; /* We have a line -> return. */ } /* Check for error returned in ftp_check_msg(). */ if (ret == -1) return FTPERR; /* if zero bytes were found that means the server has closed the connection*/ if (ret == 0) *(szptr) = '\0'; else *(szptr + 1) = '\0'; proz_debug(_("Received: %s"), line); return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_ascii(connection_t * connection) { uerr_t err; err = ftp_send_msg(connection, "TYPE A\r\n"); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] != '2') return FTPUNKNOWNTYPE; return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_binary(connection_t * connection) { uerr_t err; err = ftp_send_msg(connection, "TYPE I\r\n"); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] != '2') return FTPUNKNOWNTYPE; return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_port(connection_t * connection, const char *command) { uerr_t err; err = ftp_send_msg(connection, command); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] != '2') return FTPPORTERR; return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_list(connection_t * connection, const char *file) { uerr_t err; err = ftp_send_msg(connection, "LIST %s\r\n", file); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (ftp_get_return(connection->serv_ret_lines->line) == 550) { return FTPNSFOD; } /*TODO Fix this up, any other return code with 5xx is a fatal error */ if (connection->serv_ret_lines->line[0] == '5') return FTPERR; if (connection->serv_ret_lines->line[0] != '1') return FTPERR; return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_retr(connection_t * connection, const char *file) { uerr_t err; err = ftp_send_msg(connection, "RETR %s\r\n", file); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] == '5') return FTPNSFOD; if (connection->serv_ret_lines->line[0] != '1') return FTPERR; return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_pasv(connection_t * connection, unsigned char *addr) { uerr_t err; unsigned char *p; int i; err = ftp_send_msg(connection, "PASV\r\n"); if (err != FTPOK) return err; err = ftp_get_reply(connection); proz_debug(_("FTP PASV Header = %s"), connection->serv_ret_lines->line); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] != '2') return FTPNOPASV; /* Parse it. */ p = (unsigned char *)connection->serv_ret_lines->line; for (p += 4; *p && !isdigit(*p); p++) ; if (!*p) return FTPINVPASV; for (i = 0; i < 6; i++) { addr[i] = 0; for (; isdigit(*p); p++) addr[i] = (*p - '0') + 10 * addr[i]; if (*p == ',') p++; else if (i < 5) { return FTPINVPASV; } } return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_rest(connection_t * connection, off_t bytes) { uerr_t err; err = ftp_send_msg(connection, "REST %lld\r\n", bytes); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] != '3') return FTPRESTFAIL; return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_cwd(connection_t * connection, const char *dir) { uerr_t err; err = ftp_send_msg(connection, "CWD %s\r\n", dir); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] == '5') { /* Is it due to the file being not found? */ if (strstr(connection->serv_ret_lines->line, "o such file") || strstr(connection->serv_ret_lines->line, "o Such File") || strstr(connection->serv_ret_lines->line, "ot found") || strstr(connection->serv_ret_lines->line, "ot Found")) return FTPNSFOD; } if (connection->serv_ret_lines->line[0] != '2') return FTPCWDFAIL; return FTPOK; } /****************************************************************************** Returns the current working directory in dir. ******************************************************************************/ uerr_t ftp_pwd(connection_t * connection, char *dir) { uerr_t err; char *r, *l; char szBuffer[FTP_BUFFER_SIZE]; err = ftp_send_msg(connection, "PWD\r\n"); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] == '5') return FTPPWDERR; if (connection->serv_ret_lines->line[0] != '2') return FTPPWDFAIL; if ((r = strrchr(connection->serv_ret_lines->line, '"')) != NULL) { l = strchr(connection->serv_ret_lines->line, '"'); if ((l != NULL) && (l != r)) { *r = '\0'; ++l; strcpy(dir, l); *r = '"'; } } else { if ((r = strchr(connection->serv_ret_lines->line, ' ')) != NULL) { *r = '\0'; strcpy(dir, szBuffer); *r = ' '; } } return FTPOK; } /****************************************************************************** Returns the size of the file in size, on error size will be -1. ******************************************************************************/ uerr_t ftp_size(connection_t * connection, const char *file, off_t *size) { uerr_t err; *size = -1; err = ftp_send_msg(connection, "SIZE %s\r\n", file); if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; /* Now lets figure out what happened. */ if (connection->serv_ret_lines->line[0] == '2') { sscanf(connection->serv_ret_lines->line + 3, "%lld", size); return FTPOK; } else if (connection->serv_ret_lines->line[0] == '5') /* An error occured. */ { if (ftp_get_return(connection->serv_ret_lines->line) == 550) { return FTPNSFOD; } /* Is it due to the file being not found? */ if (strstr(connection->serv_ret_lines->line, "o such file") || strstr(connection->serv_ret_lines->line, "o Such File") || strstr(connection->serv_ret_lines->line, "ot found") || strstr(connection->serv_ret_lines->line, "ot Found")) return FTPNSFOD; } return FTPSIZEFAIL; } /****************************************************************************** Connect to the given FTP server. ******************************************************************************/ uerr_t ftp_connect_to_server(connection_t * connection, const char *name, int port) { uerr_t err; err = connect_to_server(&(connection->ctrl_sock), name, port, &connection->conn_timeout); if (err != NOCONERROR) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; if (connection->serv_ret_lines->line[0] != '2') return FTPCONREFUSED; return FTPOK; } /****************************************************************************** This function will call bind() to return a bound socket then the FTP server will be connected with a port request and asked to connect. ******************************************************************************/ uerr_t ftp_get_listen_socket(connection_t * connection, int *listen_sock) { /* Get a fixed value. */ char command[MAX_MSG_SIZE]; int sockfd; socklen_t len; struct sockaddr_in TempAddr; char *port, *ipaddr; struct sockaddr_in serv_addr; uerr_t err; if (bind_socket(&sockfd) != BINDOK) return LISTENERR; len = sizeof(serv_addr); if (getsockname(sockfd, (struct sockaddr *)&serv_addr, &len) < 0) { perror("getsockname"); close(sockfd); return CONPORTERR; } /* Get hosts info. */ len = sizeof(TempAddr); if (getsockname(connection->ctrl_sock, (struct sockaddr *)&TempAddr, &len) < 0) { perror("getsockname"); close(sockfd); return CONPORTERR; } ipaddr = (char *)&TempAddr.sin_addr; port = (char *)&serv_addr.sin_port; #define UC(b) (((int)b) & 0xff) sprintf(command, "PORT %d,%d,%d,%d,%d,%d\r\n", UC(ipaddr[0]), UC(ipaddr[1]), UC(ipaddr[2]), UC(ipaddr[3]), UC(port[0]), UC(port[1])); err = ftp_port(connection, command); if (err != FTPOK) return err; *listen_sock = sockfd; return FTPOK; } /****************************************************************************** ... ******************************************************************************/ uerr_t ftp_login(connection_t * connection, const char *username, const char *passwd) { uerr_t err = FTPERR; int ret_code = 220; boolean logged_in = FALSE; while (1) { switch (ret_code) { case 220: /* IDEA: Lets add the proxy support here. */ if (!ftp_use_proxy(connection)) { /* No proxy just direct connection. */ err = ftp_send_msg(connection, "USER %s\r\n", username); } else { switch (connection->ftp_proxy->type) { case USERatSITE: err = ftp_send_msg(connection, "USER %s@%s:%d\r\n", username, connection->u.host, connection->u.port); break; case USERatPROXYUSERatSITE: err = ftp_send_msg(connection, "USER %s@%s@%s:%d\r\n", username, connection->ftp_proxy->username, connection->u.host, connection->u.port); break; case USERatSITE_PROXYUSER: err = ftp_send_msg(connection, "USER %s:%d@%s %s\r\n", username, connection->u.host, connection->u.port, connection->ftp_proxy->username); break; case PROXYUSERatSITE: err = ftp_send_msg(connection, "USER %s@%s:%d\r\n", connection->ftp_proxy->username, connection->u.host, connection->u.port); break; default: /* Something else, just send PROXY USER. */ err = ftp_send_msg(connection, "USER %s\r\n", connection->ftp_proxy->username); break; } } if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; break; case 230: /* Fallthrough. */ case 231: /* Fallthrough. */ case 202: logged_in = TRUE; if (!ftp_use_proxy(connection)) return FTPOK; /* Logged in succesfully. */ switch (connection->ftp_proxy->type) { case LOGINthenUSERatSITE: err = ftp_send_msg(connection, "USER %s@%s:%d\r\n", username, connection->u.host, connection->u.port); break; case OPENSITE: err = ftp_send_msg(connection, "OPEN %s:%d\r\n", connection->u.host, connection->u.port); break; case SITESITE: err = ftp_send_msg(connection, "SITE %s:%d\r\n", connection->u.host, connection->u.port); break; case PROXYUSERatSITE: err = ftp_send_msg(connection, "USER %s\r\n", username); break; default: /* TODO What is the default here? */ return FTPOK; break; } if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; break; /* Handle 421 services not available. */ case 421: return FTPSERVCLOSEDATLOGIN; break; /* User name is all right, need password. */ case 331: if (!ftp_use_proxy(connection)) { /* No proxy just direct connection. */ err = ftp_send_msg(connection, "PASS %s\r\n", passwd); } else { switch (connection->ftp_proxy->type) { case USERatSITE: err = ftp_send_msg(connection, "PASS %s\r\n", passwd); break; case USERatPROXYUSERatSITE: err = ftp_send_msg(connection, "PASS %s@%s\r\n", passwd, connection->ftp_proxy->passwd); break; case USERatSITE_PROXYUSER: err = ftp_send_msg(connection, "PASS %s\r\n", passwd); break; case PROXYUSERatSITE: err = ftp_send_msg(connection, "PASS %s\r\n", connection->ftp_proxy->passwd); break; default: /* Something else we dont know about. */ err = ftp_send_msg(connection, "PASS %s\r\n", connection->ftp_proxy->passwd); break; } } if (err != FTPOK) return err; err = ftp_get_reply(connection); if (err != FTPOK) return err; break; /* 5xx series of commands indicate error. */ case 530: return FTPLOGREFUSED; break; case 501: /* Fallthrough. */ case 503: /* Fallthrough. */ case 550: return FTPERR; break; default: /* Unknown error code. */ proz_debug(_("Unknown code %d retuned during FTP login"), ret_code); return FTPERR; break; } ret_code = ftp_get_return(connection->serv_ret_lines->line); done_with_response(connection); } if (err != FTPOK) return err; return FTPOK; } /****************************************************************************** ... ******************************************************************************/ boolean ftp_use_proxy(connection_t * connection) { return (connection->ftp_proxy && connection->ftp_proxy->use_proxy && connection->ftp_proxy->proxy_url.url) ? TRUE : FALSE; } /****************************************************************************** Gets info about the url (connection->u) from the FTP server, and fills in info like whether the server supports resume, the file size etc. ******************************************************************************/ uerr_t proz_ftp_get_url_info(connection_t * connection) { uerr_t err; char *user, *passwd, *tmp; netrc_entry *netrc_ent; boolean passive_mode; longstring buffer; boolean size_ok; struct ftpparse fp; /* if we have to use a HTTP proxy call the routine which is defined in http.c and just return. */ if (ftp_use_proxy(connection) && connection->ftp_proxy->type == HTTPPROXY) { err = ftp_get_url_info_from_http_proxy(connection); return err; } init_response(connection); if (ftp_use_proxy(connection)) { connection_show_message(connection, _("Connecting to %s"), connection->ftp_proxy->proxy_url.host); /* Connect to the proxy server here. */ err = ftp_connect_to_server(connection, connection->ftp_proxy->proxy_url.host, connection->ftp_proxy->proxy_url.port); if (err != FTPOK) { connection_show_message(connection, _("Error while connecting to %s"), connection->ftp_proxy->proxy_url.host); return err; } connection_show_message(connection, _("Connected to %s"), connection->ftp_proxy->proxy_url.host); } else { connection_show_message(connection, _("Connecting to %s"), connection->u.host); err = ftp_connect_to_server(connection, connection->u.host, connection->u.port); if (err != FTPOK) { connection_show_message(connection, _("Error while connecting to %s"), connection->u.host);; return err; } connection_show_message(connection, _("Connected to %s"), connection->u.host); } done_with_response(connection); user = connection->u.user; passwd = connection->u.passwd; /* Use .netrc if asked to do so. */ if (connection->use_netrc == TRUE) { netrc_ent = search_netrc(libprozrtinfo.netrc_list, connection->u.host); if (netrc_ent != NULL) { user = netrc_ent->account; passwd = netrc_ent->password; } } user = user ? user : libprozrtinfo.ftp_default_user; passwd = passwd ? passwd : libprozrtinfo.ftp_default_passwd; if (strcmp(user, "anonymous") == 0) connection_show_message(connection, _("Logging in as user %s with password %s"), user, passwd); else { int pwd_len = strlen(passwd); char *tmp_pwd = (char *)kmalloc(pwd_len + 1); memset(tmp_pwd, 'x', pwd_len); tmp_pwd[pwd_len] = 0; connection_show_message(connection, _("Logging in as user %s with password %s"), user, tmp_pwd); kfree(tmp_pwd); } init_response(connection); err = ftp_login(connection, user, passwd); if (err != FTPOK) { close_sock(&connection->ctrl_sock); return err; } done_with_response(connection); connection_show_message(connection, _("Logged in successfully")); init_response(connection); err = ftp_binary(connection); if (err != FTPOK) { close_sock(&connection->ctrl_sock); return err; } done_with_response(connection); /* Do we need to CWD? */ if (*connection->u.dir) { init_response(connection); err = ftp_cwd(connection, connection->u.dir); if (err != FTPOK) { connection_show_message(connection, _("CWD failed to change to directory '%s'"), connection->u.dir); close_sock(&connection->ctrl_sock); return err; } else { done_with_response(connection); } } else connection_show_message(connection, _("CWD not needed")); init_response(connection); err = ftp_rest(connection, 0); if (err != FTPOK) { connection->resume_support = FALSE; connection_show_message(connection, _("REST failed")); /* NOTE: removed return err; */ } else { connection->resume_support = TRUE; connection_show_message(connection, _("REST ok")); } done_with_response(connection); /* Lets see whether the URL really is a file. */ init_response(connection); err = ftp_cwd(connection, connection->u.file); if (err == FTPOK) { /* So connection->u.file is a directory and not a file. */ connection->file_type = DIRECTORY; return FTPOK; } else { /* FIXME: The statement below is strictly not true, it could be a symlink but for the moment lets leave this as it is, later we will perform a LIST command and detect whether it is a symlink. */ connection->file_type = REGULAR_FILE; } done_with_response(connection); init_response(connection); err = ftp_size(connection, connection->u.file, &connection->main_file_size); /* if ((err == FTPOK) || (err == FTPNSFOD) || (err != FTPSIZEFAIL)) */ /* { */ /* close_sock(&connection->ctrl_sock); */ /* return err; */ /* } */ switch (err) { case FTPNSFOD: { close_sock(&connection->ctrl_sock); return err; } case FTPOK: size_ok = TRUE; break; case FTPSIZEFAIL: size_ok = FALSE; break; default: size_ok = FALSE; } done_with_response(connection); /* Now we additionaly will get the server to display info with the list command, initially we only called the LIST command only if the SIZE failed */ err = ftp_setup_data_sock_1(connection, &passive_mode); if (err != FTPOK) { close_sock(&connection->ctrl_sock); return err; } init_response(connection); err = ftp_ascii(connection); if (err != FTPOK) { close_sock(&connection->ctrl_sock); return err; } done_with_response(connection); init_response(connection); err = ftp_list(connection, connection->u.file); if (err != FTPOK) { if (err == FTPNSFOD) { //If the remote server returns ftpnsfod which could be due //to the fact that the server doesnt permit the directory //contents to be listed we will print a warning and return //FTPOK as it is not a fatal error. connection_show_message(connection, _("FTP LIST failed: File not found or access not permitted.")); close_sock(&connection->ctrl_sock); return FTPOK; } else { connection_show_message(connection, "FTP LIST failed: Server returned %s", connection->serv_ret_lines->line); close_sock(&connection->ctrl_sock); return FTPOK; } } done_with_response(connection); err = ftp_setup_data_sock_2(connection, &passive_mode); if (err != FTPOK) { close_sock(&connection->ctrl_sock); return err; } /* Now read the data to the buffer. */ /* TODO Create a buffer which dynamically resizes itself as we add data. */ if (krecv(connection->data_sock, buffer, sizeof(buffer), 0, &connection->xfer_timeout) == -1) { connection_show_message(connection, _("Error receiving FTP transfer data: %s"), strerror(errno)); return FTPERR; } proz_debug(_("String received after the LIST command = %s"), buffer); while ((tmp = strrchr(buffer, '\n')) || (tmp = strrchr(buffer, '\r'))) { *tmp = 0; } ; close_sock(&connection->data_sock); close_sock(&connection->ctrl_sock); // size_rt = size_returner(buffer, strlen(buffer)); err = ftp_parse(&fp, buffer, strlen(buffer)); if (err != FTPPARSEOK) { connection_show_message(connection, _ ("Unable to parse the line the FTP server returned:please report URL to prozilla-dev@disconnected-by-peer.at ")); } if (err == FTPPARSEOK) { proz_debug("size returned from LIST %ld", fp.filesize); //SEC size_rt off_t? if (size_ok == FALSE) { proz_debug("SIZE failed, setting file size based on LIST"); connection->main_file_size = fp.filesize; } } return FTPOK; } /****************************************************************************** This will be the first step in setting up a data sock, it will try PASV or PORT. ******************************************************************************/ uerr_t ftp_setup_data_sock_1(connection_t * connection, boolean * passive_mode) { uerr_t err; /* If enabled lets try PASV. */ if (connection->ftp_use_pasv == TRUE) { init_response(connection); err = ftp_pasv(connection, connection->pasv_addr); /* If the error is due to the server not supporting PASV then set the flag and lets try PORT. */ if ((err == FTPNOPASV) || (err == FTPINVPASV)) { proz_debug(_("Server doesn't seem to support PASV")); *passive_mode = FALSE; } else if (err == FTPOK) /* Server supports PASV. */ { char dhost[256]; unsigned short dport; sprintf(dhost, "%d.%d.%d.%d", connection->pasv_addr[0], connection->pasv_addr[1], connection->pasv_addr[2], connection->pasv_addr[3]); dport = (connection->pasv_addr[4] << 8) + connection->pasv_addr[5]; err = connect_to_server(&connection->data_sock, dhost, dport, &connection->xfer_timeout); if (err != NOCONERROR) return err; /* Everything seems to be ok. */ *passive_mode = TRUE; } else return err; done_with_response(connection); } else *passive_mode = FALSE; /* Ok... Since PASV is not to be used. */ if (*passive_mode == FALSE) { /* Obtain a listen socket. */ err = ftp_get_listen_socket(connection, &connection->listen_sock); if (err != FTPOK) return err; } return FTPOK; } /****************************************************************************** This will be the second step in setting up a data sock, if passive mode is FALSE, it will call accept_connection(). ******************************************************************************/ uerr_t ftp_setup_data_sock_2(connection_t * connection, boolean * passive_mode) { uerr_t err; if (*passive_mode == FALSE) /* We have to accept the connection. */ { err = accept_connection(connection->listen_sock, &connection->data_sock); if (err != ACCEPTOK) return err; } return FTPOK; } /*Loops for connection->attempts */ uerr_t ftp_get_url_info_loop(connection_t * connection) { pthread_mutex_lock(&connection->access_mutex); connection->running = TRUE; pthread_mutex_unlock(&connection->access_mutex); assert(connection->attempts >= 0); do { if (connection->attempts > 0 && connection->err != NEWLOCATION) { connection_show_message(connection, _("Retrying attempt %d in %d seconds"), connection->attempts, connection->retry_delay.tv_sec); delay_ms(connection->retry_delay.tv_sec * 1000); } /*Push the handler which will cleanup any sockets that are left open */ pthread_cleanup_push(cleanup_socks, (void *)connection); connection->err = proz_ftp_get_url_info(connection); /*pop the handler */ pthread_cleanup_pop(0); connection->attempts++; switch (connection->err) { case FTPOK: connection_show_message(connection, _("Successfully got info")); pthread_mutex_lock(&connection->access_mutex); connection->running = FALSE; pthread_mutex_unlock(&connection->access_mutex); return connection->err; break; case FTPNSFOD: connection_show_message(connection, _("File not found!")); pthread_mutex_lock(&connection->access_mutex); connection->running = FALSE; pthread_mutex_unlock(&connection->access_mutex); return connection->err; break; default: connection_show_message(connection, proz_strerror(connection->err)); break; } } while ((connection->attempts < connection->max_attempts) || connection->max_attempts == 0); connection_show_message(connection, _ ("I have tried %d attempt(s) and have failed, aborting"), connection->attempts); pthread_mutex_lock(&connection->access_mutex); connection->running = FALSE; pthread_mutex_unlock(&connection->access_mutex); return connection->err; }