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