home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1999 May / pcp151c.iso / misc / src / install / ftp.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-09-13  |  10.1 KB  |  480 lines

  1. #if HAVE_CONFIG_H
  2. # include "config.h"
  3. # include "miscfn.h"
  4. #else
  5. /* #define HAVE_MACHINE_TYPES_H 1 */
  6. #define HAVE_ALLOCA_H 1
  7. #define HAVE_NETINET_IN_SYSTM_H 1
  8. #define HAVE_SYS_SOCKET_H 1
  9. #endif
  10.  
  11. #if HAVE_MACHINE_TYPES_H
  12. # include <machine/types.h>
  13. #endif
  14.  
  15. #if HAVE_ALLOCA_H
  16. # include <alloca.h>
  17. #endif
  18.  
  19. #if HAVE_SYS_SOCKET_H
  20. # include <sys/socket.h>
  21. #endif
  22.  
  23. #if HAVE_NETINET_IN_SYSTM_H
  24. # include <sys/types.h>
  25. # include <netinet/in_systm.h>
  26. #endif
  27.  
  28. #if ! HAVE_HERRNO
  29. extern int h_errno;
  30. #endif
  31.  
  32. #include <ctype.h>
  33. #include <errno.h>
  34. #include <fcntl.h>
  35. #include <netdb.h>
  36. #include <pwd.h>
  37. #include <stdarg.h>
  38. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <string.h>
  41. #include <sys/socket.h>
  42. #include <sys/time.h>
  43. #include <sys/types.h>
  44. #include <unistd.h>
  45.  
  46. #include <netinet/in.h>
  47. #include <netinet/ip.h>
  48. #include <arpa/inet.h>
  49.  
  50. #include "inet_aton.h"        /* for systems too stupid to provide this */
  51. #include "intl.h"
  52.  
  53. #define TIMEOUT_SECS 60
  54. #define BUFFER_SIZE 4096
  55.  
  56. #ifndef IPPORT_FTP
  57. # define IPPORT_FTP 21
  58. #endif
  59.  
  60. #if defined(USE_ALT_DNS) && USE_ALT_DNS 
  61. #include "dns.h"
  62. #endif
  63.  
  64. #include "ftp.h"
  65.  
  66. static int ftpCheckResponse(int sock, char ** str);
  67. static int ftpCommand(int sock, char * command, ...);
  68. static int ftpReadData(int sock, int out);
  69. static int getHostAddress(const char * host, struct in_addr * address);
  70.  
  71. static int ftpCheckResponse(int sock, char ** str) {
  72.     static char buf[BUFFER_SIZE + 1];
  73.     int bufLength = 0; 
  74.     fd_set emptySet, readSet;
  75.     char * chptr, * start;
  76.     struct timeval timeout;
  77.     int bytesRead, rc = 0;
  78.     int doesContinue = 1;
  79.     char errorCode[4];
  80.  
  81.     errorCode[0] = '\0';
  82.     
  83.     do {
  84.     FD_ZERO(&emptySet);
  85.     FD_ZERO(&readSet);
  86.     FD_SET(sock, &readSet);
  87.  
  88.     timeout.tv_sec = TIMEOUT_SECS;
  89.     timeout.tv_usec = 0;
  90.     
  91.     rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
  92.     if (rc < 1) {
  93.         if (rc==0) 
  94.         return FTPERR_BAD_SERVER_RESPONSE;
  95.         else
  96.         rc = FTPERR_UNKNOWN;
  97.     } else
  98.         rc = 0;
  99.  
  100.     bytesRead = read(sock, buf + bufLength, sizeof(buf) - bufLength - 1);
  101.  
  102.     bufLength += bytesRead;
  103.  
  104.     buf[bufLength] = '\0';
  105.  
  106.     /* divide the response into lines, checking each one to see if 
  107.        we are finished or need to continue */
  108.  
  109.     start = chptr = buf;
  110.  
  111.     do {
  112.         while (*chptr != '\n' && *chptr) chptr++;
  113.  
  114.         if (*chptr == '\n') {
  115.         *chptr = '\0';
  116.         if (*(chptr - 1) == '\r') *(chptr - 1) = '\0';
  117.         if (str) *str = start;
  118.  
  119.         if (errorCode[0]) {
  120.             if (!strncmp(start, errorCode, 3) && start[3] == ' ')
  121.             doesContinue = 0;
  122.         } else {
  123.             strncpy(errorCode, start, 3);
  124.             errorCode[3] = '\0';
  125.             if (start[3] != '-') {
  126.             doesContinue = 0;
  127.             } 
  128.         }
  129.  
  130.         start = chptr + 1;
  131.         chptr++;
  132.         } else {
  133.         chptr++;
  134.         }
  135.     } while (*chptr);
  136.  
  137.     if (doesContinue && chptr > start) {
  138.         memcpy(buf, start, chptr - start - 1);
  139.         bufLength = chptr - start - 1;
  140.     } else {
  141.         bufLength = 0;
  142.     }
  143.     } while (doesContinue && !rc);
  144.  
  145.     if (*errorCode == '4' || *errorCode == '5') {
  146.     if (!strncmp(errorCode, "550", 3)) {
  147.         return FTPERR_FILE_NOT_FOUND;
  148.     }
  149.  
  150.     return FTPERR_BAD_SERVER_RESPONSE;
  151.     }
  152.  
  153.     if (rc) return rc;
  154.  
  155.     return 0;
  156. }
  157.  
  158. int ftpCommand(int sock, char * command, ...) {
  159.     va_list ap;
  160.     int len;
  161.     char * s;
  162.     char * buf;
  163.     int rc;
  164.  
  165.     va_start(ap, command);
  166.     len = strlen(command) + 2;
  167.     s = va_arg(ap, char *);
  168.     while (s) {
  169.     len += strlen(s) + 1;
  170.     s = va_arg(ap, char *);
  171.     }
  172.     va_end(ap);
  173.  
  174.     buf = alloca(len + 1);
  175.  
  176.     va_start(ap, command);
  177.     strcpy(buf, command);
  178.     strcat(buf, " ");
  179.     s = va_arg(ap, char *);
  180.     while (s) {
  181.     strcat(buf, s);
  182.     strcat(buf, " ");
  183.     s = va_arg(ap, char *);
  184.     }
  185.     va_end(ap);
  186.  
  187.     buf[len - 2] = '\r';
  188.     buf[len - 1] = '\n';
  189.     buf[len] = '\0';
  190.      
  191.     if (write(sock, buf, len) != len) {
  192.         return FTPERR_SERVER_IO_ERROR;
  193.     }
  194.  
  195.     if ((rc = ftpCheckResponse(sock, NULL)))
  196.     return rc;
  197.  
  198.     return 0;
  199. }
  200.  
  201. #if !defined(USE_ALT_DNS) || !USE_ALT_DNS 
  202. static int mygethostbyname(const char * host, struct in_addr * address) {
  203.     struct hostent * hostinfo;
  204.  
  205.     hostinfo = gethostbyname(host);
  206.     if (!hostinfo) return 1;
  207.  
  208.     memcpy(address, hostinfo->h_addr_list[0], hostinfo->h_length);
  209.     return 0;
  210. }
  211. #endif
  212.  
  213. static int getHostAddress(const char * host, struct in_addr * address) {
  214.     if (isdigit(host[0])) {
  215.       if (!inet_aton(host, address)) {
  216.       return FTPERR_BAD_HOST_ADDR;
  217.       }
  218.     } else {
  219.       if (mygethostbyname(host, address)) {
  220.       errno = h_errno;
  221.       return FTPERR_BAD_HOSTNAME;
  222.       }
  223.     }
  224.     
  225.     return 0;
  226. }
  227.  
  228. int ftpOpen(char * host, char * name, char * password, char * proxy,
  229.         int port) {
  230.     static int sock;
  231.     /*static char * lastHost = NULL;*/
  232.     struct in_addr serverAddress;
  233.     struct sockaddr_in destPort;
  234.     struct passwd * pw;
  235.     char * buf;
  236.     int rc;
  237.  
  238.     if (port < 0) port = IPPORT_FTP;
  239.  
  240.     if (!name)
  241.     name = "anonymous";
  242.  
  243.     if (!password) {
  244.     if (getuid()) {
  245.         pw = getpwuid(getuid());
  246.         password = alloca(strlen(pw->pw_name) + 2);
  247.         strcpy(password, pw->pw_name);
  248.         strcat(password, "@");
  249.     } else {
  250.         password = "root@";
  251.     }
  252.     }
  253.  
  254.     if (proxy) {
  255.     buf = alloca(strlen(name) + strlen(host) + 5);
  256.     sprintf(buf, "%s@%s", name, host);
  257.     name = buf;
  258.     host = proxy;
  259.     }
  260.  
  261.     if ((rc = getHostAddress(host, &serverAddress))) return rc;
  262.  
  263.     sock = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
  264.     if (sock < 0) {
  265.         return FTPERR_FAILED_CONNECT;
  266.     }
  267.  
  268.     destPort.sin_family = AF_INET;
  269.     destPort.sin_port = htons(port);
  270.     destPort.sin_addr = serverAddress;
  271.  
  272.     if (connect(sock, (struct sockaddr *) &destPort, sizeof(destPort))) {
  273.     close(sock);
  274.         return FTPERR_FAILED_CONNECT;
  275.     }
  276.  
  277.     /* ftpCheckResponse() assumes the socket is nonblocking */
  278.     if (fcntl(sock, F_SETFL, O_NONBLOCK)) {
  279.     close(sock);
  280.         return FTPERR_FAILED_CONNECT;
  281.     }
  282.  
  283.     if ((rc = ftpCheckResponse(sock, NULL))) {
  284.         return rc;     
  285.     }
  286.  
  287.     if ((rc = ftpCommand(sock, "USER", name, NULL))) {
  288.     close(sock);
  289.     return rc;
  290.     }
  291.  
  292.     if ((rc = ftpCommand(sock, "PASS", password, NULL))) {
  293.     close(sock);
  294.     return rc;
  295.     }
  296.  
  297.     if ((rc = ftpCommand(sock, "TYPE", "I", NULL))) {
  298.     close(sock);
  299.     return rc;
  300.     }
  301.  
  302.     return sock;
  303. }
  304.  
  305. int ftpReadData(int sock, int out) {
  306.     char buf[BUFFER_SIZE];
  307.     fd_set emptySet, readSet;
  308.     struct timeval timeout;
  309.     int bytesRead, rc;
  310.     
  311.     while (1) {
  312.     FD_ZERO(&emptySet);
  313.     FD_ZERO(&readSet);
  314.     FD_SET(sock, &readSet);
  315.  
  316.     timeout.tv_sec = TIMEOUT_SECS;
  317.     timeout.tv_usec = 0;
  318.     
  319.     rc = select(sock + 1, &readSet, &emptySet, &emptySet, &timeout);
  320.     if (rc == 0) {
  321.         close(sock);
  322.         return FTPERR_SERVER_TIMEOUT;
  323.     } else if (rc < 0) {
  324.         close(sock);
  325.         return FTPERR_UNKNOWN;
  326.     }
  327.  
  328.     bytesRead = read(sock, buf, sizeof(buf));
  329.     if (bytesRead == 0) {
  330.         close(sock);
  331.         return 0;
  332.     }
  333.  
  334.     if (write(out, buf, bytesRead) != bytesRead) {
  335.         close(sock);
  336.         return FTPERR_FILE_IO_ERROR;
  337.     }
  338.     }
  339. }
  340.  
  341. int ftpGetFileDesc(int sock, char * remotename) {
  342.     int dataSocket;
  343.     struct sockaddr_in dataAddress;
  344.     int i, j;
  345.     char * passReply;
  346.     char * chptr;
  347.     char * retrCommand;
  348.     int rc;
  349.  
  350.     if (write(sock, "PASV\r\n", 6) != 6) {
  351.         return FTPERR_SERVER_IO_ERROR;
  352.     }
  353.     if ((rc = ftpCheckResponse(sock, &passReply)))
  354.     return FTPERR_PASSIVE_ERROR;
  355.  
  356.     chptr = passReply;
  357.     while (*chptr && *chptr != '(') chptr++;
  358.     if (*chptr != '(') return FTPERR_PASSIVE_ERROR; 
  359.     chptr++;
  360.     passReply = chptr;
  361.     while (*chptr && *chptr != ')') chptr++;
  362.     if (*chptr != ')') return FTPERR_PASSIVE_ERROR;
  363.     *chptr-- = '\0';
  364.  
  365.     while (*chptr && *chptr != ',') chptr--;
  366.     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
  367.     chptr--;
  368.     while (*chptr && *chptr != ',') chptr--;
  369.     if (*chptr != ',') return FTPERR_PASSIVE_ERROR;
  370.     *chptr++ = '\0';
  371.     
  372.     /* now passReply points to the IP portion, and chptr points to the
  373.        port number portion */
  374.  
  375.     dataAddress.sin_family = AF_INET;
  376.     if (sscanf(chptr, "%d,%d", &i, &j) != 2) {
  377.     return FTPERR_PASSIVE_ERROR;
  378.     }
  379.     dataAddress.sin_port = htons((i << 8) + j);
  380.  
  381.     chptr = passReply;
  382.     while (*chptr++) {
  383.     if (*chptr == ',') *chptr = '.';
  384.     }
  385.  
  386.     if (!inet_aton(passReply, &dataAddress.sin_addr)) 
  387.     return FTPERR_PASSIVE_ERROR;
  388.  
  389.     dataSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
  390.     if (dataSocket < 0) {
  391.         return FTPERR_FAILED_CONNECT;
  392.     }
  393.  
  394.     retrCommand = alloca(strlen(remotename) + 20);
  395.     sprintf(retrCommand, "RETR %s\r\n", remotename);
  396.     i = strlen(retrCommand);
  397.    
  398.     if (write(sock, retrCommand, i) != i) {
  399.         return FTPERR_SERVER_IO_ERROR;
  400.     }
  401.  
  402.     if (connect(dataSocket, (struct sockaddr *) &dataAddress, 
  403.             sizeof(dataAddress))) {
  404.     close(dataSocket);
  405.         return FTPERR_FAILED_DATA_CONNECT;
  406.     }
  407.  
  408.     if ((rc = ftpCheckResponse(sock, NULL))) {
  409.     close(dataSocket);
  410.     return rc;
  411.     }
  412.  
  413.     return dataSocket;
  414. }
  415.  
  416. int ftpGetFileDone(int sock) {
  417.     if (ftpCheckResponse(sock, NULL)) {
  418.     return FTPERR_BAD_SERVER_RESPONSE;
  419.     }
  420.  
  421.     return 0;
  422. }
  423.  
  424. int ftpGetFile(int sock, char * remotename, int dest) {
  425.     int dataSocket, rc;
  426.  
  427.     dataSocket = ftpGetFileDesc(sock, remotename);
  428.     if (dataSocket < 0) return dataSocket;
  429.  
  430.     rc = ftpReadData(dataSocket, dest);
  431.     close(dataSocket);
  432.     
  433.     if (rc) return rc;
  434.  
  435.     return ftpGetFileDone(sock);
  436. }
  437.  
  438. void ftpClose(int sock) {
  439.     close(sock);
  440. }
  441.  
  442. const char *ftpStrerror(int errorNumber) {
  443.   switch (errorNumber) {
  444.     case FTPERR_BAD_SERVER_RESPONSE:
  445.       return _("Bad FTP server response");
  446.  
  447.     case FTPERR_SERVER_IO_ERROR:
  448.       return _("FTP IO error");
  449.  
  450.     case FTPERR_SERVER_TIMEOUT:
  451.       return _("FTP server timeout");
  452.  
  453.     case FTPERR_BAD_HOST_ADDR:
  454.       return _("Unable to lookup FTP server host address");
  455.  
  456.     case FTPERR_BAD_HOSTNAME:
  457.       return _("Unable to lookup FTP server host name");
  458.  
  459.     case FTPERR_FAILED_CONNECT:
  460.       return _("Failed to connect to FTP server");
  461.  
  462.     case FTPERR_FAILED_DATA_CONNECT:
  463.       return _("Failed to establish data connection to FTP server");
  464.  
  465.     case FTPERR_FILE_IO_ERROR:
  466.       return _("IO error to local file");
  467.  
  468.     case FTPERR_PASSIVE_ERROR:
  469.       return _("Error setting remote server to passive mode");
  470.  
  471.     case FTPERR_FILE_NOT_FOUND:
  472.       return _("File not found on server");
  473.  
  474.     case FTPERR_UNKNOWN:
  475.     default:
  476.       return _("FTP Unknown or unexpected error");
  477.   }
  478. }
  479.   
  480.