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