home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 13 / CDA13.ISO / MISC / SRC / INSTALL / FTP.C < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-23  |  9.5 KB  |  449 lines

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