"Fossies" - the Fresh Open Source Software Archive

Member "sitecopy-0.16.6/src/ftp.c" (30 Dec 2005, 33793 Bytes) of archive /linux/www/sitecopy-0.16.6.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ftp.c" see the Fossies "Dox" file reference documentation.

    1 /* 
    2    sitecopy, for managing remote web sites. Generic(ish) FTP routines.
    3    Copyright (C) 1998-2005, Joe Orton <joe@manyfish.co.uk>
    4                                                                      
    5    This program is free software; you can redistribute it and/or modify
    6    it under the terms of the GNU General Public License as published by
    7    the Free Software Foundation; either version 2 of the License, or
    8    (at your option) any later version.
    9   
   10    This program is distributed in the hope that it will be useful,
   11    but WITHOUT ANY WARRANTY; without even the implied warranty of
   12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   13    GNU General Public License for more details.
   14   
   15    You should have received a copy of the GNU General Public License
   16    along with this program; if not, write to the Free Software
   17    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  
   18 
   19 */
   20 
   21 /* This contains an FTP client implementation.
   22  * It performs transparent connection management - it it dies,
   23  * it will be reconnected automagically.
   24  */
   25 
   26 #include <config.h>
   27 
   28 #include <sys/types.h>
   29 
   30 #include <sys/socket.h>
   31 #include <sys/stat.h>
   32 #include <netinet/in.h>
   33 
   34 #include <ctype.h>
   35 #include <errno.h>
   36 #include <fcntl.h>
   37 #include <stdio.h>
   38 #include <time.h>
   39 #ifdef HAVE_LIMITS_H
   40 #include <limits.h>     /* for PATH_MAX */
   41 #endif
   42 #ifdef HAVE_STRING_H
   43 #include <string.h>
   44 #endif
   45 #ifdef HAVE_STRINGS_H
   46 #include <strings.h>
   47 #endif 
   48 #ifdef HAVE_STDLIB_H
   49 #include <stdlib.h>
   50 #endif
   51 #ifdef HAVE_UNISTD_H
   52 #include <unistd.h>
   53 #endif
   54 #include <assert.h>
   55 
   56 #include <ne_alloc.h>
   57 #include <ne_string.h>
   58 #include <ne_socket.h>
   59 
   60 #include "common.h"
   61 #include "frontend.h"
   62 #include "ftp.h"
   63 #include "protocol.h"
   64 #include "i18n.h"
   65 #include "lsparser.h"
   66 
   67 struct ftp_session_s {
   68     /* User options */
   69     unsigned int use_passive;
   70     unsigned int echo_quit;
   71     unsigned int use_cwd; /* CWD before STOR */
   72 
   73     int connected; /* true when open */
   74 
   75     /* Userdata passed to fe_authenticate call */
   76     void *feauth_userdata;
   77     
   78     const char *hostname; /* server hostname for fe_login */
   79 
   80     /* DTP connection: */
   81     ne_socket *dtpsock;
   82     unsigned short dtp_port;
   83     ne_inet_addr *dtp_addr;
   84     
   85     /* PI connection: */
   86     ne_sock_addr *pi_addr;
   87     unsigned short pi_port;
   88     ne_socket *pisock;
   89     const ne_inet_addr *pi_curaddr; /* currently used iaddr */
   90 
   91     /* Current file transfor mode. */
   92     enum tran_mode {
   93     tran_unknown, /* whatever the server picked */
   94     tran_binary,
   95     tran_ascii
   96     } mode;
   97 
   98     enum rfc2428_mode {
   99         rfc2428_unknown = 0, /* RFC24248 support unknown */
  100         rfc2428_ok, /* RFC2428 is supported */
  101         rfc2428_bad  /* RFC2428 is not supported */
  102     } rfc2428;
  103     
  104 #ifndef PATH_MAX
  105 #define PATH_MAX 2048
  106 #endif
  107     /* Stores the current working dir on the remote server */
  108     char cwd[PATH_MAX];
  109 
  110     /* time from MDTM response... bit crap having this here. */
  111     time_t get_modtime;
  112     
  113     /* remember these... we may have to log in more than once. */
  114     char username[FE_LBUFSIZ], password[FE_LBUFSIZ];
  115 
  116     unsigned int echo_response:1;
  117 
  118     /* Reply buffer */
  119     char rbuf[BUFSIZ];
  120 
  121     /* Error string */
  122     char error[BUFSIZ];
  123 };
  124 
  125 #define FTP_ERR(x) do { \
  126 int _ftp_err = (x); if (_ftp_err != FTP_OK) return _ftp_err; } while (0)
  127 
  128 /* Sets error string */
  129 static void ftp_seterror(ftp_session *sess, const char *error);
  130 
  131 /* Opens the data connection */
  132 static int ftp_data_open(ftp_session *sess, const char *command, ...) 
  133     ne_attribute((format (printf, 2, 3)));
  134 
  135 static int get_modtime(ftp_session *sess, const char *root,
  136                const char *filename);
  137 
  138 /* Sets session error to system errno value 'errnum'. */
  139 static void set_syserr(ftp_session *sess, const char *error, int errnum)
  140 {
  141     ne_snprintf(sess->error, sizeof sess->error, "%s: %s", error, 
  142         strerror(errnum));
  143     NE_DEBUG(DEBUG_FTP, "FTP Error set: %s\n", sess->error);
  144 }
  145 
  146 /* Sets the error string using the error from the given socket. */
  147 static void set_sockerr(ftp_session *sess, const ne_socket *sock, 
  148             const char *doing, ssize_t errnum)
  149 {
  150     switch (errnum) {
  151     case NE_SOCK_CLOSED:
  152     ne_snprintf(sess->error, BUFSIZ, 
  153          _("%s: connection was closed by server."), doing);
  154     break;
  155     case NE_SOCK_TIMEOUT:
  156     ne_snprintf(sess->error, BUFSIZ, 
  157             _("%s: connection timed out."), doing);
  158     break;
  159     default:
  160     ne_snprintf(sess->error, BUFSIZ, "%s: %s", doing, ne_sock_error(sock));
  161     break;
  162     }
  163     NE_DEBUG(DEBUG_FTP, "ftp: Set socket error (%" NE_FMT_SSIZE_T "): %s\n",
  164              errnum, sess->error);
  165 }
  166 
  167 /* set_pisockerr must be called to handle any PI socket error to
  168  * ensure that the connection can be correctly re-opened later.  Pass
  169  * DOING as the operation which failed, ERRNUM as the error from the
  170  * ne_sock_* layer. */
  171 static void set_pisockerr(ftp_session *sess, const char *doing, ssize_t errnum)
  172 {
  173     set_sockerr(sess, sess->pisock, doing, errnum);
  174     NE_DEBUG(DEBUG_FTP, "PI socket closed: %s\n", sess->error);
  175     ne_sock_close(sess->pisock);
  176     sess->pisock = NULL;
  177     sess->connected = 0;
  178 }
  179 
  180 #define FTP_PI_BROKEN(r) \
  181 ((r) == NE_SOCK_RESET || (r) == NE_SOCK_CLOSED || (r) == NE_SOCK_TIMEOUT)
  182 
  183 /* Read the reply to an FTP command on the PI connection using given
  184  * buffer.  Returns FTP_BROKEN if the PI connection has been broken
  185  * since the last command was executed. (due to a timeout etc) */
  186 static int read_reply(ftp_session *sess, int *code, char *buf, size_t bufsiz)
  187 {
  188     int multiline = 0;
  189 
  190     *code = 0;
  191 
  192     do {
  193         ssize_t ret = ne_sock_readline(sess->pisock, buf, bufsiz-1);
  194     if (ret < 0) {
  195             set_pisockerr(sess, _("Could not read response line"), ret);
  196             return FTP_PI_BROKEN(ret) ? FTP_BROKEN : FTP_ERROR;
  197     }
  198     
  199         buf[ret] = '\0';
  200     NE_DEBUG(DEBUG_FTP, "< %s", buf);
  201         
  202         if (ret > 4 && isdigit(buf[0]) && isdigit(buf[1]) && isdigit(buf[2])) {
  203             *code = atoi(buf);
  204             if (multiline == 0 && buf[3] == '-') {
  205                 /* Begin multiline response. */
  206                 multiline = *code;
  207             } else if (multiline == *code && buf[3] == ' ') {
  208                 /* End multiline response. */
  209                 multiline = 0;
  210             }
  211         }
  212 
  213     } while (multiline);
  214 
  215     return FTP_OK;
  216 }
  217 
  218 /* Parses the 213 response to a MDTM command... on success, returns
  219  * FTP_MODTIME and sets modtime to the time in the response.  On
  220  * failute, returns FTP_ERROR. */
  221 static int parse_modtime(ftp_session *sess, char *response, time_t *modtime) 
  222 {
  223     struct tm t = {0};
  224 
  225     ne_shave(response, "\r\n");
  226     NE_DEBUG(DEBUG_FTP, "Parsing modtime: %s\n", response);
  227 
  228     if (strlen(response) != 18) {
  229         ftp_seterror(sess, _("Cannot parse MDTM response; wrong length."));
  230     return FTP_ERROR;
  231     }
  232 
  233     if (sscanf(response, "213 %4d%2d%2d" "%2d%2d%2d",
  234                &t.tm_year, &t.tm_mon, &t.tm_mday,
  235                &t.tm_hour, &t.tm_min, &t.tm_sec) < 6) {
  236         ftp_seterror(sess, _("Cannot parse MDTM response."));
  237     return FTP_ERROR;
  238     }
  239 
  240     t.tm_year -= 1900; /* years since 1900 */
  241     t.tm_mon -= 1; /* zero-based */
  242     t.tm_isdst = -1;
  243 
  244     *modtime = mktime(&t);
  245 
  246     NE_DEBUG(DEBUG_FTP, "Converted to: %s", ctime(modtime));
  247     return FTP_MODTIME;
  248 }
  249 
  250 /* Parses the response to a PASV command.  Sets DTP port and address
  251  * in session structure. Returns FTP_ERROR on failure. */
  252 static int parse_pasv_reply(ftp_session *sess, char *reply)
  253 {
  254     unsigned int h1, h2, h3, h4, p1, p2;
  255     unsigned char addr[4];
  256     char *start;
  257 
  258     start = strchr(reply, ' ');
  259     if (start == NULL) {
  260         ftp_seterror(sess, _("Could not find address in PASV response"));
  261     return FTP_ERROR;
  262     }
  263     while (! isdigit(*start) && (*start != '\0'))
  264     start++;
  265     /* get the host + port */
  266     if (sscanf(start, "%u,%u,%u,%u,%u,%u", &h1, &h2, &h3, &h4, &p1, &p2) < 6) {
  267         ftp_seterror(sess, _("Could not parse PASV response"));
  268     return FTP_ERROR;
  269     }
  270     addr[0] = h1 & 0xff; addr[1] = h2 & 0xff;
  271     addr[2] = h3 & 0xff; addr[3] = h4 & 0xff;
  272     /* Record this for future reference */
  273     sess->dtp_port = (p1<<8) | p2;
  274     sess->dtp_addr = ne_iaddr_make(ne_iaddr_ipv4, addr);
  275     if (sess->dtp_addr == NULL) {
  276         ftp_seterror(sess, _("Invalid IP address in PASV response"));
  277     return FTP_ERROR;
  278     }
  279 
  280     NE_DEBUG(DEBUG_FTP, "ftp: Parsed PASV response, using port %d\n", sess->dtp_port);
  281 
  282     return FTP_PASSIVE;
  283 }
  284 
  285 /* parse an EPSV reply */
  286 static int parse_epasv_reply(ftp_session *sess, char *reply)
  287 {
  288     char *ptr = strchr(reply, '('), *eptr;
  289     long port;
  290 
  291     /* reply syntax: "229 fFOO (|||port|)" where | can be any character
  292      * in ASCII from 33-126, obscurely. */
  293     if (ptr == NULL || ptr[1] != ptr[2] || ptr[1] != ptr[3]
  294         || (eptr = strchr(ptr + 4, ')')) == NULL
  295         || eptr == ptr + 4 || eptr[-1] != ptr[1]) {
  296         ftp_seterror(sess,_("Malformed EPSV response"));
  297         return FTP_ERROR;
  298     }
  299 
  300     eptr[-1] = '\0';
  301 
  302     port = strtol(ptr + 4, &eptr, 10);
  303     if (*eptr != '\0') {
  304         ftp_seterror(sess, _("Malformed port in EPSV response"));
  305         return FTP_ERROR;
  306     }
  307 
  308     sess->dtp_addr = (ne_inet_addr *)sess->pi_curaddr;
  309     sess->dtp_port = (unsigned short)port;
  310     
  311     NE_DEBUG(DEBUG_FTP, "ftp: Parsed EPSV response, using port %ld\n", port);
  312 
  313     return FTP_PASSIVE;
  314 }
  315 
  316 /* Parse a reply of given 'code' (which is pre-parsed). */
  317 static int parse_reply(ftp_session *sess, int code, char *reply)
  318 {
  319     /* Special case reply parsing: */
  320     switch (code) {
  321     case 200: /* misc OK codes */
  322     case 220:
  323     case 230:
  324     case 250: /* completed file action */
  325     case 253: /* delete successful */
  326     case 257: /* mkdir success */
  327     return FTP_OK;
  328     case 226: /* received file okay */
  329     return FTP_SENT;
  330     case 150: /* file transfer... ready for data */
  331     case 125:
  332     return FTP_READY;
  333     case 550: /* couldn't complete file action */
  334     return FTP_FILEBAD;
  335     case 331: /* got username, want password */
  336     return FTP_NEEDPASSWORD;
  337     case 350: /* file action pending further info - RNFR */
  338     return FTP_FILEMORE;
  339     case 221:
  340     /* succesful QUIT reseponse. */
  341     return FTP_CLOSED;
  342     case 421: /* service denied; PI connection closure */
  343         ne_sock_close(sess->pisock);
  344         sess->pisock = NULL;
  345         sess->connected = 0;
  346         return FTP_BROKEN;
  347     case 553: /* couldn't create directory */
  348     return FTP_ERROR;
  349     case 213: /* MDTM response, hopefully */
  350     return parse_modtime(sess, reply, &sess->get_modtime);
  351     case 227: /* PASV response, hopefully */
  352     return parse_pasv_reply(sess, reply); 
  353     case 229: /* EPSV response */
  354         return parse_epasv_reply(sess, reply);
  355     default:
  356         return FTP_ERROR;
  357     }
  358 }
  359 
  360 /* Runs FTP command 'cmd', and reads the response.  Returns FTP_BROKEN
  361  * if the PI connection broke due to timeout etc.  execute() should be
  362  * used instead of this function for normal FTP commands. */
  363 static int run_command(ftp_session *sess, const char *cmd)
  364 {
  365     char *line = ne_concat(cmd, "\r\n", NULL);
  366     int code, ret;
  367 
  368 #ifdef NE_DEBUGGING
  369     if (strncmp(cmd, "PASS ", 4) == 0
  370         && (ne_debug_mask & NE_DBG_HTTPPLAIN) == 0) {
  371         NE_DEBUG(DEBUG_FTP, "> PASS ...\n");
  372     } else {
  373         NE_DEBUG(DEBUG_FTP, "> %s\n", cmd);
  374     }
  375 #endif
  376 
  377     /* Send the command. */
  378     ret = ne_sock_fullwrite(sess->pisock, line, strlen(line));
  379     free(line);
  380 
  381     if (ret < 0) {
  382         set_pisockerr(sess, "Could not send command", ret);
  383         return FTP_PI_BROKEN(ret) ? FTP_BROKEN : FTP_ERROR;
  384     }
  385     
  386     /* Read the response. */
  387     ret = read_reply(sess, &code, sess->rbuf, sizeof sess->rbuf);
  388     if (ret != FTP_OK) return ret;
  389 
  390     /* Set reply as default error string. */
  391     ftp_seterror(sess, sess->rbuf);
  392 
  393     /* Parse the reply. */
  394     return parse_reply(sess, code, sess->rbuf);
  395 }    
  396 
  397 /* Wrapper for run_command: runs an FTP command using printf template
  398  * and arguments, handling PI connection timeouts as necessary. */
  399 static int execute(ftp_session *sess, const char *template, ...) 
  400 {
  401     va_list params;
  402     int tries = 0, ret;
  403     char buf[1024];
  404 
  405     va_start(params, template);
  406     ne_vsnprintf(buf, sizeof buf, template, params);
  407     va_end(params);
  408 
  409     do {
  410         ret = ftp_open(sess);
  411         if (ret != FTP_OK) return ret;
  412 
  413         ret = run_command(sess, buf);
  414     } while (ret == FTP_BROKEN && ++tries < 3);
  415 
  416     /* Don't let FTP_BROKEN get out */
  417     if (ret == FTP_BROKEN) 
  418     ret = FTP_ERROR;
  419     return ret;
  420 }
  421 
  422 /* Dump the given filename down the DTP socket, performing ASCII line
  423  * ending translation. Returns zero on success or non-zero on error
  424  * (in which case, session error string is set). */
  425 static int send_file_ascii(ftp_session *sess, FILE *f, off_t fsize)
  426 {
  427     char buffer[BUFSIZ];
  428     off_t total = 0, lasttotal = 0;
  429 
  430     while (fgets(buffer, BUFSIZ - 1, f) != NULL) {
  431         size_t buflen;
  432         char *pnt;
  433         int ret;
  434 
  435         pnt = strchr(buffer, '\r');
  436         if (pnt == NULL)
  437             pnt = strchr(buffer, '\n');
  438         if (pnt) {
  439             *pnt++ = '\r';
  440             *pnt++ = '\n';
  441             buflen = pnt - buffer;
  442         } else {
  443             /* line with no CRLF. */
  444             buflen = strlen(buffer);
  445         }
  446         
  447     ret = ne_sock_fullwrite(sess->dtpsock, buffer, buflen);
  448     if (ret) {
  449         set_sockerr(sess, sess->dtpsock, _("Error sending file"), ret);
  450         return -1;
  451     }
  452     total += (pnt - buffer) + 2;
  453     /* Only call progress every 4K otherwise it is way too 
  454      * chatty. */
  455     if (total > lasttotal + 4096) {
  456         lasttotal = total;
  457         fe_transfer_progress(total, fsize);
  458     }
  459     }
  460 
  461     if (ferror(f)) {
  462     int errnum = errno;
  463     set_syserr(sess, _("Error reading file"), errnum);
  464     return -1;
  465     }
  466 
  467     return 0;
  468 }
  469 
  470 /* Send file 'f' (of size 'size') down DTP socket. */
  471 static int send_file_binary(ftp_session *sess, FILE *f, off_t size)
  472 {
  473     char buffer[BUFSIZ];
  474     size_t ret;
  475     off_t total = 0;
  476     
  477     while ((ret = fread(buffer, 1, sizeof buffer, f)) > 0) {
  478     int rv = ne_sock_fullwrite(sess->dtpsock, buffer, ret);
  479     if (rv) {
  480         set_sockerr(sess, sess->dtpsock, _("Could not send file"), rv);
  481         return -1;
  482     }
  483     total += ret;
  484     fe_transfer_progress(total, size);
  485     }
  486 
  487     if (ferror(f)) {
  488     int errnum = errno;
  489     set_syserr(sess, _("Error reading file"), errnum);
  490     return -1;
  491     }
  492     
  493     return 0;
  494 }
  495 
  496 /* Dump from given socket into given file.  Returns number of bytes
  497  * written on success, or -1 on error (session error string is
  498  * set). */
  499 static int receive_file(ftp_session *sess, FILE *f)
  500 {
  501     ssize_t bytes;
  502     off_t count = 0;
  503     char buffer[BUFSIZ];
  504 
  505     while ((bytes = ne_sock_read(sess->dtpsock, buffer, BUFSIZ)) > 0) {
  506     count += bytes;
  507     fe_transfer_progress(count, -1);
  508     if (fwrite(buffer, 1, bytes, f) < (size_t)bytes) {
  509         int errnum = errno;
  510         set_syserr(sess, _("Error writing to file"), errnum);
  511         return -1;
  512     }
  513     }
  514 
  515     if (bytes < 0 && bytes != NE_SOCK_CLOSED) {
  516     set_sockerr(sess, sess->dtpsock, _("Receiving file"), bytes);
  517     return -1;
  518     }
  519 
  520     return 0;
  521 }
  522 
  523 /* Passively (client-connects) open the DTP socket ; return non-zero
  524  * on success. */
  525 static int dtp_open_passive(ftp_session *sess) 
  526 {
  527     int ret;
  528     sess->dtpsock = ne_sock_create();
  529     ret = ne_sock_connect(sess->dtpsock, sess->dtp_addr, sess->dtp_port);
  530     if (ret) {
  531     set_sockerr(sess, sess->dtpsock, 
  532                     _("Could not connect passive data socket"), ret);
  533         ne_sock_close(sess->dtpsock);
  534     return 0;
  535     } else {
  536     return 1;
  537     }
  538 }
  539 
  540 /* Initializes the driver + connection.
  541  * Returns FTP_OK on success, or FTP_LOOKUP if the hostname
  542  * could not be resolved.
  543  */
  544 ftp_session *ftp_init(void)
  545 {
  546     ftp_session *sess = ne_calloc(sizeof(ftp_session));
  547     sess->connected = 0;
  548     sess->mode = tran_unknown;
  549     return sess;
  550 }
  551 
  552 void ftp_set_passive(ftp_session *sess, int use_passive) 
  553 {
  554     sess->use_passive = use_passive;
  555 }
  556 
  557 void ftp_set_usecwd(ftp_session *sess, int use_cwd) 
  558 {
  559     sess->use_cwd = use_cwd;
  560 }
  561 
  562 int ftp_set_server(ftp_session *sess, struct site_host *server)
  563 {
  564     if (server->username) {
  565     strcpy(sess->username, server->username);
  566     }
  567     if (server->password) {
  568     strcpy(sess->password, server->password);
  569     }
  570     sess->hostname = server->hostname;
  571     sess->pi_port = server->port;
  572     fe_connection(fe_namelookup, server->hostname);
  573     sess->pi_addr = ne_addr_resolve(server->hostname, 0);
  574     if (ne_addr_result(sess->pi_addr)) {
  575     char buf[256];
  576     ne_snprintf(sess->error, sizeof sess->error,
  577             "Could not resolve server `%s': %s", server->hostname,
  578             ne_addr_error(sess->pi_addr, buf, sizeof buf));
  579     return FTP_LOOKUP;
  580     }
  581     return FTP_OK;
  582 }
  583 
  584 /* Cleans up and closes the control connection.
  585  * Returns FTP_OK if the connection is closed, else FTP_ERROR;
  586  */
  587 int ftp_finish(ftp_session *sess)
  588 {
  589     int ret = FTP_OK;
  590     int old_er = sess->echo_response;
  591     if (sess->connected) {
  592     sess->echo_response = sess->echo_quit;
  593     if (run_command(sess, "QUIT") != FTP_CLOSED) {
  594         ret = FTP_ERROR;
  595     }
  596         if (sess->pisock) ne_sock_close(sess->pisock);
  597         sess->connected = 0;
  598     sess->echo_response = old_er;
  599     }
  600     return ret;
  601 }
  602 
  603 /* Creates the given directory
  604  * FTP state response */
  605 int ftp_mkdir(ftp_session *sess, const char *dir)
  606 {
  607     return execute(sess, "MKD %s", dir);
  608 }
  609  
  610 /* Renames or moves a file */
  611 int ftp_move(ftp_session *sess, const char *from, const char *to)
  612 {
  613     if (execute(sess, "RNFR %s", from) == FTP_FILEMORE) {
  614     return execute(sess, "RNTO %s", to);
  615     }
  616     return FTP_ERROR;
  617 }
  618 
  619 int ftp_delete(ftp_session *sess, const char *filename)
  620 {
  621     return execute(sess, "DELE %s", filename);
  622 }
  623 
  624 int ftp_rmdir(ftp_session *sess, const char *filename)
  625 {
  626     return execute(sess, "RMD %s", filename);
  627 }
  628 
  629 /* Actively open the data connection, running command COMMAND; the
  630  * client then listens for and accepts the connection from the server.
  631  * On successful return, the DTP connection is open. */
  632 static int dtp_open_active(ftp_session *sess, const char *command)
  633 {
  634     char *a, *p;
  635     int ret;
  636     int listener;
  637     ksize_t alen;
  638     struct sockaddr_in addr;
  639 
  640     ret = ftp_open(sess);
  641     if (ret != FTP_OK) return ret;
  642     
  643     alen = sizeof(addr);
  644     if (getsockname(ne_sock_fd(sess->pisock), 
  645             (struct sockaddr *)&addr, &alen) < 0) {
  646     int errnum = errno;
  647     set_syserr(sess, _("Active open failed: could not determine "
  648                "address of control socket"), errnum);
  649         return FTP_ERROR;
  650     }
  651 
  652     /* Let the kernel choose a port */
  653     addr.sin_port = 0;
  654  
  655     /* Create a local socket to accept the connection on */
  656     listener = socket(AF_INET, SOCK_STREAM, 0);
  657     if (listener < 0) {
  658     int errnum = errno;
  659     set_syserr(sess, _("Active open failed: could not create socket"),
  660            errnum);
  661     return FTP_ERROR;
  662     }
  663 
  664     /* Bind it to an address. */
  665     if (bind(listener, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
  666     int errnum = errno;
  667     set_syserr(sess, _("Active open failed: could not bind to address"),
  668            errnum);
  669     (void) close(listener);
  670     return FTP_ERROR;
  671     }
  672 
  673     /* Retrieve the address again; determine which port was chosen. */
  674     alen = sizeof(addr);
  675     if (getsockname(listener, (struct sockaddr *)&addr, &alen) < 0) {
  676     int errnum = errno;
  677     set_syserr(sess, _("Active open failed: could not determine address "
  678                "of data socket"), errnum);
  679     (void) close(listener);
  680     return FTP_ERROR;
  681     }
  682 
  683     if (addr.sin_port == 0) {
  684         ftp_seterror(sess, _("Could not determine bound port number for "
  685                              "data socket"));
  686         close(listener);
  687         return FTP_ERROR;
  688     }
  689 
  690     if (listen(listener, 1) < 0) {
  691     int errnum = errno;
  692     set_syserr(sess, ("Active open failed: could not listen for "
  693               "connection"), errnum);
  694     (void) close(listener);
  695     return FTP_ERROR;
  696     }
  697 
  698 #define UC(b)   (((int)b)&0xff)
  699     a = (char *)&addr.sin_addr.s_addr;
  700     p = (char *)&addr.sin_port;
  701 
  702     /* Execute the PORT command */
  703     ret = execute(sess, "PORT %d,%d,%d,%d,%d,%d",
  704             UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]),
  705             UC(p[0]), UC(p[1]));
  706     if (ret != FTP_OK) {
  707     /* Failed to execute the PORT command - close the socket */
  708     NE_DEBUG(DEBUG_FTP, "PORT command failed.\n");
  709     close(listener);
  710     return ret;
  711     }
  712 
  713     /* Send the command.  This will make the remote end
  714      * initiate the connection.
  715      */
  716     ret = execute(sess, "%s", command);
  717     
  718     /* Do they want it? */
  719     if (ret != FTP_READY) {
  720     NE_DEBUG(DEBUG_FTP, "Command failed.\n");
  721     } else {
  722     /* Now wait for a connection from the remote end. */
  723     sess->dtpsock = ne_sock_create();
  724     if (ne_sock_accept(sess->dtpsock, listener)) {
  725         int errnum = errno;
  726         set_syserr(sess,
  727                _("Active open failed: could not accept connection"),
  728                errnum);
  729             ne_sock_close(sess->dtpsock);
  730         ret = FTP_ERROR;
  731     }
  732     }
  733 
  734     (void) close(listener);
  735     return ret;
  736 }
  737 
  738 int ftp_chmod(ftp_session *sess, const char *filename, const mode_t mode)
  739 {
  740     return execute(sess, "SITE CHMOD %03o %s", mode & 0777, filename);
  741 }
  742 
  743 /* default to always trying EPSV if using neon 0.24, which shouldn't
  744  * hurt too much since it falls back on PASV on failure anyway. */
  745 #if NE_VERSION_MINOR == 24
  746 #define ne_iaddr_typeof(ia) (ne_iaddr_ipv6)
  747 #endif
  748 
  749 /* Open the DATA connection using whichever means appropriate, running
  750  * FTP command COMMAND with printf-style arguments. */
  751 static int ftp_data_open(ftp_session *sess, const char *command, ...) 
  752 {
  753     int ret;
  754     va_list params;
  755     char buf[BUFSIZ];
  756 
  757     va_start(params, command);
  758     ne_vsnprintf(buf, BUFSIZ, command, params);
  759     va_end(params);
  760 
  761     if (sess->use_passive) {
  762         ret = FTP_ERROR;
  763 
  764         if (sess->rfc2428 != rfc2428_bad
  765             && ne_iaddr_typeof(sess->pi_curaddr) == ne_iaddr_ipv6) {
  766             ret = execute(sess, "EPSV");
  767             if (ret == FTP_PASSIVE) sess->rfc2428 = rfc2428_ok;
  768         }
  769         if ((sess->rfc2428 == rfc2428_unknown && ret != FTP_PASSIVE)
  770             || sess->rfc2428 == rfc2428_bad) {
  771             ret = execute(sess, "PASV");
  772         }
  773     if (ret == FTP_PASSIVE) {
  774         if (dtp_open_passive(sess)) {
  775         return execute(sess, "%s", buf);
  776         } else {
  777         return FTP_ERROR;
  778         }
  779     } else {
  780         return FTP_NOPASSIVE;
  781     }
  782     } else {
  783     /* we are not using passive mode. */
  784     return dtp_open_active(sess, buf);
  785     }
  786 }
  787 
  788 
  789 /* Closes the data connection.  If 'discard' is non-zero any error
  790  * string is discarded; otherwise the session error is set. */
  791 static int dtp_close(ftp_session *sess, int discard)
  792 {
  793     /* Read the response line */
  794     int ret;
  795     
  796     NE_DEBUG(DEBUG_FTP, "Closing DTP connection...\n");
  797     ret = ne_sock_close(sess->dtpsock);
  798     if (ret < 0) {
  799     int errnum = errno;
  800     if (!discard) set_syserr(sess, _("Error closing data socket"), errnum);
  801     return FTP_ERROR;
  802     } else {
  803         int code;
  804 
  805     ret = read_reply(sess, &code, sess->rbuf, sizeof sess->rbuf);
  806         if (ret != FTP_OK) return ret;
  807         
  808         ret = parse_reply(sess, code, sess->rbuf);
  809     if (ret == FTP_OK || ret == FTP_SENT)
  810         return FTP_SENT;
  811     else {
  812             if (!discard) ftp_seterror(sess, sess->rbuf);
  813         return FTP_ERROR;
  814         }
  815     }
  816 }
  817 
  818 /* Set the transfer type appropriately.
  819  * Only set it if it *needs* setting, though.
  820  * Returns FTP_OK on success, or something else otherwise. */
  821 static int set_mode(ftp_session *sess, enum tran_mode mode)
  822 {
  823     int ret;
  824 
  825     if (sess->mode == mode)
  826     return FTP_OK;
  827 
  828     ret = execute(sess, mode==tran_ascii?"TYPE A":"TYPE I");
  829     if (ret == FTP_OK)
  830     sess->mode = mode;
  831     else
  832     sess->mode = tran_unknown;
  833 
  834     return ret;
  835 }
  836 
  837 /* Change dir before uploading if necessary; some servers (notably
  838  * some version of ProFTPD) will reject a STOR if it is not in the
  839  * CWD.  *remotefile is updated to be the filename relative to the
  840  * working directory.
  841  *
  842  * 5, 7 Apr 2002, Volker Kuhlmann <VolkerKuhlmann@GMX.de> */
  843 static int maybe_chdir(ftp_session *sess, const char **remotefile)
  844 {
  845     int ret;
  846     const char *slash, *fn = *remotefile;
  847     char dir[PATH_MAX];
  848 
  849     if (!sess->use_cwd || fn[0] != '/' || strlen(fn) > PATH_MAX)
  850     return FTP_OK;
  851 
  852     slash = strrchr(fn, '/'); /* can't be NULL since fn[0] == '/'. */
  853     *remotefile = slash + 1;
  854 
  855     ne_strnzcpy(dir, fn, 1 + slash - fn);
  856 
  857     if (strcmp(dir, sess->cwd)) {
  858     ret = execute(sess, "CWD %s", dir);
  859     if (ret == FTP_OK) {
  860         NE_DEBUG(DEBUG_FTP, "Stored new CWD as %s\n", dir);
  861         strcpy(sess->cwd, dir);
  862     }
  863     } else {
  864     NE_DEBUG(DEBUG_FTP, "CWD not needed.\n");
  865     ret = FTP_OK;
  866     }
  867 
  868     return ret;
  869 }
  870 
  871 
  872 /* upload the given file */
  873 int ftp_put(ftp_session *sess, 
  874         const char *localfile, const char *remotefile, int ascii) 
  875 {
  876     int ret;
  877     struct stat st;
  878     FILE *f;
  879     int tries = 0, dret;
  880 
  881     /* Set the transfer type correctly */
  882     if (set_mode(sess, ascii?tran_ascii:tran_binary))
  883     return FTP_ERROR;
  884     
  885     f = fopen(localfile, "r" FOPEN_BINARY_FLAGS);
  886     if (f == NULL) {
  887     int errnum = errno;
  888     set_syserr(sess, _("Could not open file"), errnum);
  889     return FTP_ERROR;
  890     }
  891 
  892     if (fstat(fileno(f), &st) < 0) {
  893     int errnum = errno;
  894     set_syserr(sess, _("Could not determine length of file"),
  895              errnum);
  896     fclose(f);
  897     return FTP_ERROR;
  898     }
  899     
  900     do {
  901         /* Rewind the file pointer for attempts other than the
  902          * first: */
  903         if (tries && fseek(f, 0, SEEK_SET) < 0) {
  904             int errnum = errno;
  905             set_syserr(sess, _("Could not rewind to beginning of file"),
  906                        errnum);
  907             break;
  908         }
  909 
  910         ret = maybe_chdir(sess, &remotefile);
  911         if (ret != FTP_OK) {
  912             fclose(f);
  913             return ret;
  914         }
  915 
  916         ret = ftp_data_open(sess, "STOR %s", remotefile);
  917         if (ret == FTP_READY) {
  918             if (ascii)
  919                 ret = send_file_ascii(sess, f, st.st_size);
  920             else
  921                 ret = send_file_binary(sess, f, st.st_size);
  922 
  923             /* Now close the DTP connection and read the response. */
  924             dret = dtp_close(sess, 0);
  925 
  926             if (dret == FTP_SENT && ret == 0) {
  927                 fclose(f);
  928                 return FTP_OK;
  929             }
  930         } else {
  931             /* ftp_data_open() will already have retried as necessary
  932              * so don't retry it again here if that was all that
  933              * failed. */
  934             dret = FTP_ERROR;
  935         }
  936     } while (dret == FTP_BROKEN && ++tries < 3);
  937 
  938     fclose(f);
  939     return FTP_ERROR;
  940 }
  941 
  942 /* Conditionally upload the given file */
  943 int ftp_put_cond(ftp_session *sess, const char *localfile, 
  944          const char *remotefile, int ascii, const time_t mtime)
  945 {
  946     int ret;
  947     
  948     ret = get_modtime(sess, remotefile, "");
  949     if (ret != FTP_OK) {
  950     return ret;
  951     }
  952     
  953     /* Is the retrieved time different from the given time? */
  954     if (sess->get_modtime != mtime) {
  955     return FTP_FAILED;
  956     }
  957 
  958     /* Do a normal upload */
  959     return ftp_put(sess, localfile, remotefile, ascii);
  960 }
  961 
  962 /* Slightly dodgy ftp_get in that you need to know the remote file
  963  * size. Works, but not very generic. */
  964 int ftp_get(ftp_session *sess, const char *localfile, const char *remotefile, 
  965         int ascii) 
  966 {
  967     int ret;
  968     FILE *f = fopen(localfile, "wb");
  969 
  970     if (f == NULL) {
  971     int errnum = errno;
  972     set_syserr(sess, _("Could not open file"), errnum);
  973     return FTP_ERROR;
  974     }
  975 
  976     if (set_mode(sess, ascii?tran_ascii:tran_binary))
  977     return FTP_ERROR;
  978 
  979     if (ftp_data_open(sess, "RETR %s", remotefile) == FTP_READY) {
  980         int clo, errnum = 0;
  981 
  982     /* Receive the file */
  983     ret = receive_file(sess, f);
  984     clo = fclose(f);
  985         if (clo) errnum = errno;
  986 
  987     if (dtp_close(sess, 0) == FTP_SENT && ret == 0 && clo == 0) {
  988         /* Success! */
  989         return FTP_OK;
  990     } else if (clo) {
  991             set_syserr(sess, _("Error writing to file"), errnum);
  992         }
  993             
  994     }
  995 
  996     return FTP_ERROR;
  997 }
  998 
  999 int 
 1000 ftp_read_file(ftp_session *sess, const char *remotefile,
 1001           ne_block_reader reader, void *userdata) 
 1002 {
 1003     ssize_t ret;
 1004 
 1005     /* Always use binary mode */
 1006     if (set_mode(sess, tran_binary))
 1007     return FTP_ERROR;
 1008 
 1009     if (ftp_data_open(sess, "RETR %s", remotefile) == FTP_READY) {
 1010     char buffer[BUFSIZ];
 1011     while ((ret = ne_sock_read(sess->dtpsock, buffer, sizeof buffer))
 1012            > 0)
 1013         reader(userdata, buffer, ret);
 1014     if ((dtp_close(sess, 0) == FTP_SENT) && (ret == NE_SOCK_CLOSED)) {
 1015         return FTP_OK;
 1016     }
 1017     }
 1018     return FTP_ERROR;
 1019 }
 1020 
 1021 /* Log in to the server.  This routine *cannot* use execute() to
 1022  * prevent recursion; if the connection times out during
 1023  * authentication then that is a fatal error. */
 1024 static int authenticate(ftp_session *sess)
 1025 {
 1026     int ret;
 1027     char buf[1024];
 1028     
 1029     /* Fetch creds from user if necessary. */
 1030     if (strlen(sess->username) == 0 || strlen(sess->password) == 0) {
 1031     if (fe_login(fe_login_server, NULL, sess->hostname,
 1032              sess->username, sess->password))
 1033         return FTP_ERROR;
 1034     }
 1035     NE_DEBUG(DEBUG_FTP,  "FTP: Sending 'USER %s':\n", sess->username);
 1036 
 1037     ne_snprintf(buf, sizeof buf, "USER %s", sess->username);
 1038     ret = run_command(sess, buf);
 1039     if (ret == FTP_NEEDPASSWORD) {
 1040     NE_DEBUG(DEBUG_FTP,  "FTP: Sending PASS command...\n");
 1041         ne_snprintf(buf, sizeof buf, "PASS %s", sess->password);
 1042     ret = run_command(sess, buf);
 1043     }
 1044 
 1045     return ret;
 1046 }
 1047 
 1048 int ftp_open(ftp_session *sess) 
 1049 {
 1050     int ret, code, success;
 1051     const ne_inet_addr *ia;
 1052 
 1053     if (sess->connected) return FTP_OK;
 1054     NE_DEBUG(DEBUG_FTP, "Opening socket to port %d\n", sess->pi_port);
 1055 
 1056     /* Invalidate cwd, so a CWD is always used if needed (valid cwds
 1057      * must begin with a slash. */
 1058     strcpy(sess->cwd, "x");
 1059     
 1060     /* Open TCP connection */
 1061     fe_connection(fe_connecting, NULL);
 1062     sess->pisock = ne_sock_create();
 1063     for (ia = ne_addr_first(sess->pi_addr), success = 0;
 1064      !success && ia != NULL; 
 1065      ia = ne_addr_next(sess->pi_addr)) {
 1066     success = ne_sock_connect(sess->pisock, ia, sess->pi_port) == 0;
 1067         sess->pi_curaddr = ia;
 1068     }
 1069 
 1070     if (!success) {
 1071         ne_sock_close(sess->pisock);
 1072     return FTP_CONNECT;
 1073     }
 1074 
 1075     fe_connection(fe_connected, NULL);
 1076 
 1077     /* Read the hello message */
 1078     ret = read_reply(sess, &code, sess->rbuf, sizeof sess->rbuf);
 1079     if (ret != FTP_OK) return FTP_HELLO;
 1080 
 1081     ret = parse_reply(sess, code, sess->rbuf);
 1082     if (ret != FTP_OK) {
 1083         ftp_seterror(sess, sess->rbuf);
 1084         return FTP_HELLO;
 1085     }
 1086 
 1087     if (authenticate(sess) != FTP_OK) {
 1088         if (sess->connected) {
 1089             ne_sock_close(sess->pisock);
 1090             sess->pisock = NULL;
 1091         }
 1092     return FTP_LOGIN;
 1093     }
 1094 
 1095     /* PI connection OK. */
 1096     sess->connected = 1;
 1097 
 1098     /* Restore previous transfer mode; set mode to unknown for the
 1099      * duration to avoid infinite recusion (since set_mode calls
 1100      * execute_command calls ftp_open) . */
 1101     if (sess->mode != tran_unknown) {
 1102     enum tran_mode mode = sess->mode;
 1103     sess->mode = tran_unknown;
 1104     return set_mode(sess, mode);
 1105     }
 1106 
 1107     return FTP_OK;
 1108 }
 1109 
 1110 void ftp_seterror(ftp_session *sess, const char *error)
 1111 {
 1112     memset(sess->error, 0, BUFSIZ);
 1113     strncpy(sess->error, error, BUFSIZ);
 1114 }
 1115 
 1116 const char *ftp_get_error(ftp_session *sess)
 1117 {
 1118     return sess->error;
 1119 }    
 1120 
 1121 int ftp_fetch(ftp_session *sess, const char *startdir, struct proto_file **list)
 1122 {
 1123     struct proto_file *tail = NULL;
 1124     struct ls_context *lsctx;
 1125     int ret;
 1126 
 1127     if ((ret = ftp_data_open(sess, "LIST -la %s", startdir)) != FTP_READY) {
 1128         return FTP_ERROR;
 1129     }
 1130 
 1131     lsctx = ls_init(startdir);
 1132 
 1133     ret = FTP_OK;
 1134 
 1135     do {
 1136         enum ls_result lsrv;
 1137         struct ls_file lfile;
 1138         ssize_t len;
 1139 
 1140         len = ne_sock_readline(sess->dtpsock, sess->rbuf, BUFSIZ);
 1141         if (len == NE_SOCK_CLOSED) {
 1142             NE_DEBUG(DEBUG_FTP, "ftp: EOF from DTP connection.\n");
 1143             break;
 1144         }
 1145         if (len < 0) {
 1146             set_sockerr(sess, sess->dtpsock,
 1147                         _("Could not read 'LIST' response."), len);
 1148             ret = FTP_ERROR;
 1149             break;
 1150         }
 1151 
 1152         lsrv = ls_parse(lsctx, sess->rbuf, &lfile);
 1153         if (lsrv == ls_error) {
 1154             char err[512];
 1155 
 1156             ne_snprintf(err, sizeof err, _("Parse error in LIST response: %s"),
 1157                         ls_geterror(lsctx));
 1158             NE_DEBUG(DEBUG_FTP, "ftp: ls_parse error, aborting.\n");
 1159             ftp_seterror(sess, err);
 1160             ret = FTP_ERROR;
 1161         } else {
 1162             ls_pflist_add(list, &tail, &lfile, lsrv);
 1163         }
 1164     } while (ret == FTP_OK);
 1165 
 1166     ls_destroy(lsctx);
 1167 
 1168     NE_DEBUG(DEBUG_FTP, "ftp: Fetch finished with %d.\n", ret);
 1169     if (ret == FTP_OK) {
 1170         return dtp_close(sess, 0) == FTP_SENT ? FTP_OK : FTP_ERROR;
 1171     } else {
 1172         dtp_close(sess, 1); /* ignore retval */
 1173         return ret;
 1174     }
 1175 }
 1176 
 1177 static int 
 1178 get_modtime(ftp_session *sess, const char *root, const char *filename) 
 1179 {
 1180     NE_DEBUG(DEBUG_FTP, "Getting modtime.\n");
 1181     if (execute(sess, "MDTM %s%s", root, filename) == FTP_MODTIME) {
 1182     NE_DEBUG(DEBUG_FTP, "Got modtime.\n");
 1183     return FTP_OK;
 1184     } else {
 1185     return FTP_ERROR;
 1186     }
 1187 }
 1188 
 1189 int ftp_get_modtime(ftp_session *sess, const char *filename, time_t *modtime) 
 1190 {
 1191     if (get_modtime(sess, filename, "") == FTP_OK) {
 1192     *modtime = sess->get_modtime;
 1193     return FTP_OK;
 1194     } else {
 1195     *modtime = -1;
 1196     return FTP_ERROR;
 1197     }
 1198 }
 1199 
 1200 /* Sorts out the modtimes for all the files in the list.
 1201  * Returns FTP_OK on success, else FTP_ERROR. */
 1202 int 
 1203 ftp_fetch_modtimes(ftp_session *sess, const char *rootdir, 
 1204            struct proto_file *files) 
 1205 {
 1206     struct proto_file *this_file;
 1207  
 1208     for (this_file=files; this_file!=NULL; this_file=this_file->next) {
 1209     if (this_file->type != proto_file) {
 1210         continue;
 1211     }
 1212     NE_DEBUG(DEBUG_FTP, "File: %s%s\n", rootdir, this_file->filename);
 1213     if (get_modtime(sess, rootdir, this_file->filename) == FTP_OK) {
 1214         this_file->modtime = sess->get_modtime;
 1215     } else {
 1216         NE_DEBUG(DEBUG_FTP, "Didn't get modtime.\n");
 1217         return FTP_ERROR;
 1218     }
 1219     }
 1220     NE_DEBUG(DEBUG_FTP, "Walk finished ok.\n");
 1221 
 1222     return FTP_OK;
 1223 }