home *** CD-ROM | disk | FTP | other *** search
/ Dream 52 / Amiga_Dream_52.iso / Linux / Magazine / wwwoffle-2.1.tar.gz / wwwoffle-2.1 / ftp.c < prev    next >
C/C++ Source or Header  |  1998-02-11  |  20KB  |  835 lines

  1. /***************************************
  2.   $Header: /home/amb/wwwoffle/RCS/ftp.c 1.19 1998/02/11 19:25:04 amb Exp $
  3.  
  4.   WWWOFFLE - World Wide Web Offline Explorer - Version 2.1.
  5.   Functions for getting URLs using FTP.
  6.   ******************/ /******************
  7.   Written by Andrew M. Bishop
  8.  
  9.   This file Copyright 1997,98 Andrew M. Bishop
  10.   It may be distributed under the GNU Public License, version 2, or
  11.   any higher version.  See section COPYING of the GNU Public license
  12.   for conditions under which this file may be redistributed.
  13.   ***************************************/
  14.  
  15.  
  16. #include <stdio.h>
  17. #include <stdlib.h>
  18. #include <string.h>
  19. #include <ctype.h>
  20.  
  21. #include <sys/time.h>
  22. #include <unistd.h>
  23. #include <fcntl.h>
  24.  
  25. #include "wwwoffle.h"
  26. #include "misc.h"
  27. #include "config.h"
  28. #include "errors.h"
  29. #include "sockets.h"
  30. #include "proto.h"
  31.  
  32.  
  33. /*+ Set this to 1 to see the full dialog with the FTP server. +*/
  34. #define DEBUG_FTP 0
  35.  
  36. /*+ Set to the name of the proxy if there is one. +*/
  37. static char *proxy=NULL;
  38.  
  39. /*+ The file descriptor of the socket +*/
  40. static int server_ctrl=-1,      /*+ for the control connection to the server. +*/
  41.            server_data=-1;      /*+ for the data connection to the server. +*/
  42.  
  43. /*+ A buffer to contain the reply +*/
  44. static char *bufferhead=NULL,   /*+ head. +*/
  45.             *buffer=NULL,       /*+ body. +*/
  46.             *buffertail=NULL;   /*+ tail. +*/
  47.  
  48. /*+ The number of characters in the buffer +*/
  49. static int nbufferhead=0,       /*+ in total for the head . +*/
  50.            nreadhead=0,         /*+ that have been read from the head. +*/
  51.            nbuffer=0,           /*+ in total for the body part. +*/
  52.            nread=0,             /*+ that have been read for the body. +*/
  53.            nbuffertail=0,       /*+ in total for the tail . +*/
  54.            nreadtail=0;         /*+ that have been read from the tail. +*/
  55.  
  56. static char *htmlise_dir_entry(char *line);
  57.  
  58.  
  59. /*++++++++++++++++++++++++++++++++++++++
  60.   Open a connection to get a URL using FTP.
  61.  
  62.   char *FTP_Open Returns NULL on success, a useful message on error.
  63.  
  64.   URL *Url The URL to open.
  65.   ++++++++++++++++++++++++++++++++++++++*/
  66.  
  67. char *FTP_Open(URL *Url)
  68. {
  69.  char *msg=NULL;
  70.  char *colon;
  71.  char *server_host=NULL;
  72.  int server_port=Protocols[Protocol_FTP].defport;
  73.  
  74.  /* Sort out the host. */
  75.  
  76.  proxy=WhichProxy(Url->proto,Url->host);
  77.  if(IsLocalNetHost(Url->host))
  78.     proxy=NULL;
  79.  
  80.  if(proxy)
  81.    {
  82.     server_host=(char*)malloc(strlen(proxy)+1);
  83.     strcpy(server_host,proxy);
  84.    }
  85.  else
  86.    {
  87.     server_host=(char*)malloc(strlen(Url->host)+1);
  88.     strcpy(server_host,Url->host);
  89.    }
  90.  
  91.  if((colon=strchr(server_host,':')))
  92.    {
  93.     *colon++=0;
  94.     if(*colon)
  95.        server_port=atoi(colon);
  96.    }
  97.  
  98.  /* Open the connection. */
  99.  
  100.  server_ctrl=OpenClientSocket(server_host,server_port);
  101.  init_buffer(server_ctrl);
  102.  
  103.  if(server_ctrl==-1)
  104.     msg=PrintMessage(Warning,"Cannot open the FTP control connection to %s port %d; [%!s].",server_host,server_port);
  105.  
  106.  free(server_host);
  107.  
  108.  return(msg);
  109. }
  110.  
  111.  
  112. /*++++++++++++++++++++++++++++++++++++++
  113.   Write to the server to request the URL.
  114.  
  115.   char *FTP_Request Returns NULL on success, a useful message on error.
  116.  
  117.   URL *Url The URL to get.
  118.  
  119.   char *request_head The head of the HTTP request for the URL.
  120.  
  121.   char *request_body The body of the HTTP request for the URL.
  122.   ++++++++++++++++++++++++++++++++++++++*/
  123.  
  124. char *FTP_Request(URL *Url,char *request_head,char *request_body)
  125. {
  126.  char *msg=NULL,*str=NULL;
  127.  char *path,*file=NULL;
  128.  char *host,*mimetype;
  129.  char *msg_reply=NULL;
  130.  int i,l,port_l,port_h;
  131.  char *user,*pass;
  132.  
  133.  /* Take a simple route if it is proxied. */
  134.  
  135.  if(proxy)
  136.    {
  137.     char *new_request_head=MakeRequestAuthorised(proxy,request_head);
  138.  
  139.     if(write_string(server_ctrl,new_request_head)==-1)
  140.        msg=PrintMessage(Warning,"Failed to write to remote FTP proxy [%!s].");
  141.     if(request_body && write_string(server_ctrl,request_body)==-1)
  142.        msg=PrintMessage(Warning,"Failed to write to remote FTP proxy [%!s].");
  143.  
  144.     if(request_head!=new_request_head)
  145.        free(new_request_head);
  146.  
  147.     return(msg);
  148.    }
  149.  
  150.  /* Else Sort out the path. */
  151.  
  152.  path=UrlDecode(Url->path,0);
  153.  if(path[strlen(path)-1]=='/')
  154.    {
  155.     path[strlen(path)-1]=0;
  156.     file=NULL;
  157.    }
  158.  else
  159.     for(i=strlen(path)-1;i>=0;i--)
  160.        if(path[i]=='/')
  161.          {
  162.           path[i]=0;
  163.           file=&path[i+1];
  164.           break;
  165.          }
  166.  
  167.  WhatFTPUserPass(Url->host,&user,&pass);
  168.  
  169.  /* send all the RFC959 commands. */
  170.  
  171.  server_data=-1;
  172.  
  173.  do
  174.    {
  175.     str=read_line_or_timeout(server_ctrl,str);
  176. #if DEBUG_FTP
  177.     PrintMessage(Debug,"FTP: connected; got: %s",str);
  178. #endif
  179.  
  180.     if(!file && !*path && str && isdigit(str[0]) && isdigit(str[1]) && isdigit(str[2]) && str[3]=='-')
  181.       {
  182.        if(msg_reply)
  183.          {
  184.           msg_reply=(char*)realloc((void*)msg_reply,strlen(msg_reply)+strlen(str));
  185.           strcat(msg_reply,str+4);
  186.          }
  187.        else
  188.          {
  189.           msg_reply=(char*)malloc(strlen(str));
  190.           strcpy(msg_reply,str+4);
  191.          }
  192.       }
  193.    }
  194.  while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  195.  
  196.  if(!str || atoi(str)!=220)
  197.    {
  198.     if(str)
  199.       {
  200.        char *p=str+strlen(str)-1;
  201.        while(*p=='\n' || *p=='\r') *p--=0;
  202.        msg=PrintMessage(Warning,"Got '%s' message when connected to FTP server.",str);
  203.       }
  204.     else
  205.        msg=PrintMessage(Warning,"No reply from FTP server when connected; timed out?");
  206.     return(msg);
  207.    }
  208.  
  209.  /* Login */
  210.  
  211.  if(write_formatted(server_ctrl,"USER %s\r\n",user)==-1)
  212.    {
  213.     msg=PrintMessage(Warning,"Failed to write 'USER' command to remote FTP host [%!s].");
  214.     return(msg);
  215.    }
  216.  
  217.  do
  218.    {
  219.     str=read_line_or_timeout(server_ctrl,str);
  220. #if DEBUG_FTP
  221.     PrintMessage(Debug,"FTP: sent 'USER %s'; got: %s",user,str);
  222. #endif
  223.    }
  224.  while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  225.  
  226.  if(!str || (atoi(str)!=230 && atoi(str)!=331))
  227.    {
  228.     if(str)
  229.       {
  230.        char *p=str+strlen(str)-1;
  231.        while(*p=='\n' || *p=='\r') *p--=0;
  232.        msg=PrintMessage(Warning,"Got '%s' message after sending 'USER' command to FTP server.",str);
  233.       }
  234.     else
  235.        msg=PrintMessage(Warning,"No reply from FTP server to 'USER' command; timed out?");
  236.     return(msg);
  237.    }
  238.  
  239.  if(atoi(str)==331)
  240.    {
  241.     if(write_formatted(server_ctrl,"PASS %s\r\n",pass)==-1)
  242.       {
  243.        msg=PrintMessage(Warning,"Failed to write 'PASS' command to remote FTP host [%!s].");
  244.        return(msg);
  245.       }
  246.  
  247.     do
  248.       {
  249.        str=read_line_or_timeout(server_ctrl,str);
  250. #if DEBUG_FTP
  251.        PrintMessage(Debug,"FTP: sent 'PASS %s'; got: %s",pass,str);
  252. #endif
  253.  
  254.        if(!file && !*path && str && isdigit(str[0]) && isdigit(str[1]) && isdigit(str[2]) && str[3]=='-')
  255.          {
  256.           if(msg_reply)
  257.             {
  258.              msg_reply=(char*)realloc((void*)msg_reply,strlen(msg_reply)+strlen(str));
  259.              strcat(msg_reply,str+4);
  260.             }
  261.           else
  262.             {
  263.              msg_reply=(char*)malloc(strlen(str));
  264.              strcpy(msg_reply,str+4);
  265.             }
  266.          }
  267.       }
  268.     while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  269.  
  270.     if(!str || (atoi(str)!=202 && atoi(str)!=230))
  271.       {
  272.        if(str)
  273.          {
  274.           char *p=str+strlen(str)-1;
  275.           while(*p=='\n' || *p=='\r') *p--=0;
  276.           msg=PrintMessage(Warning,"Got '%s' message after sending 'PASS' command to FTP server.",str);
  277.          }
  278.        else
  279.           msg=PrintMessage(Warning,"No reply from FTP server to 'PASS' command; timed out?");
  280.        return(msg);
  281.       }
  282.    }
  283.  
  284.  /* Change directory */
  285.  
  286.  if(write_formatted(server_ctrl,"CWD %s\r\n",*path?path:"/")==-1)
  287.    {
  288.     msg=PrintMessage(Warning,"Failed to write 'CWD' command to remote FTP host [%!s].");
  289.     return(msg);
  290.    }
  291.  
  292.  do
  293.    {
  294.     str=read_line_or_timeout(server_ctrl,str);
  295. #if DEBUG_FTP
  296.     PrintMessage(Debug,"FTP: sent 'CWD %s' got: %s",*path?path:"/",str);
  297. #endif
  298.  
  299.     if(!file && str && isdigit(str[0]) && isdigit(str[1]) && isdigit(str[2]) && str[3]=='-')
  300.       {
  301.        if(msg_reply)
  302.          {
  303.           msg_reply=(char*)realloc((void*)msg_reply,strlen(msg_reply)+strlen(str));
  304.           strcat(msg_reply,str+4);
  305.          }
  306.        else
  307.          {
  308.           msg_reply=(char*)malloc(strlen(str));
  309.           strcpy(msg_reply,str+4);
  310.          }
  311.       }
  312.    }
  313.  while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  314.  
  315.  if(!str || atoi(str)!=250)
  316.    {
  317.     if(str)
  318.       {
  319.        char *p=str+strlen(str)-1;
  320.        while(*p=='\n' || *p=='\r') *p--=0;
  321.        msg=PrintMessage(Warning,"Got '%s' message after sending 'CWD' command to FTP server.",str);
  322.       }
  323.     else
  324.        msg=PrintMessage(Warning,"No reply from FTP server to 'CWD' command; timed out?");
  325.     return(msg);
  326.    }
  327.  
  328.  /* Change directory again to see if file is a dir. */
  329.  
  330.  if(file)
  331.    {
  332.     if(write_formatted(server_ctrl,"CWD %s\r\n",file)==-1)
  333.       {
  334.        msg=PrintMessage(Warning,"Failed to write 'CWD' command to remote FTP host [%!s].");
  335.        return(msg);
  336.       }
  337.  
  338.     do
  339.       {
  340.        str=read_line_or_timeout(server_ctrl,str);
  341. #if DEBUG_FTP
  342.        PrintMessage(Debug,"FTP: sent 'CWD %s' got: %s",file,str);
  343. #endif
  344.       }
  345.     while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  346.  
  347.     if(!str || (atoi(str)!=250 && atoi(str)!=550))
  348.       {
  349.        if(str)
  350.          {
  351.           char *p=str+strlen(str)-1;
  352.           while(*p=='\n' || *p=='\r') *p--=0;
  353.           msg=PrintMessage(Warning,"Got '%s' message after sending 'CWD' command to FTP server.",str);
  354.          }
  355.        else
  356.           msg=PrintMessage(Warning,"No reply from FTP server to 'CWD' command; timed out?");
  357.        return(msg);
  358.       }
  359.  
  360.     if(atoi(str)==250)
  361.       {
  362.        char *msghead=
  363.        "HTTP/1.0 302 Is a directory\r\n"
  364.        "Content-Type: text/html\r\n"
  365.        "Location: %s/\r\n"
  366.        "\r\n";
  367.        char *msg=
  368.        "<HTML>\n"
  369.        "<HEAD>\n"
  370.        "<TITLE>FTP Directory requires trailing '/'</TITLE>\n"
  371.        "</HEAD>\n"
  372.        "<BODY>\n"
  373.        "<h1>Index of %s</h1>\n"
  374.        "You need to specify a URL with a '/' at the end for a directory\n"
  375.        "<br>\n"
  376.        "<a href=\"%s/\">%s/</a>\n";
  377.        char *msgtail=
  378.        "</BODY>\n"
  379.        "</HTML>\n";
  380.  
  381.        bufferhead=(char*)malloc(strlen(Url->link)+strlen(msghead)+1);
  382.        sprintf(bufferhead,msghead,Url->link);
  383.  
  384.        buffer=(char*)malloc(2*strlen(Url->name)+strlen(Url->link)+strlen(msg)+1);
  385.        sprintf(buffer,msg,Url->name,Url->link,Url->name);
  386.  
  387.        buffertail=msgtail;
  388.  
  389.        goto near_end;
  390.       }
  391.    }
  392.  
  393.  /* Set mode to binary or ASCII */
  394.  
  395.  if(write_formatted(server_ctrl,"TYPE %c\r\n",file?'I':'A')==-1)
  396.    {
  397.     msg=PrintMessage(Warning,"Failed to write 'TYPE' command to remote FTP host [%!s].");
  398.     return(msg);
  399.    }
  400.  
  401.  do
  402.    {
  403.     str=read_line_or_timeout(server_ctrl,str);
  404. #if DEBUG_FTP
  405.     PrintMessage(Debug,"FTP: sent 'TYPE %c'; got: %s",file?'I':'A',str);
  406. #endif
  407.    }
  408.  while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  409.  
  410.  if(!str || atoi(str)!=200)
  411.    {
  412.     if(str)
  413.       {
  414.        char *p=str+strlen(str)-1;
  415.        while(*p=='\n' || *p=='\r') *p--=0;
  416.        msg=PrintMessage(Warning,"Got '%s' message after sending 'TYPE' command to FTP server.",str);
  417.       }
  418.     else
  419.        msg=PrintMessage(Warning,"No reply from FTP server to 'TYPE' command; timed out?");
  420.     return(msg);
  421.    }
  422.  
  423.  /* Create the data connection. */
  424.  
  425.  if(write_string(server_ctrl,"PASV\r\n")==-1)
  426.    {
  427.     msg=PrintMessage(Warning,"Failed to write 'PASV' command to remote FTP host [%!s].");
  428.     return(msg);
  429.    }
  430.  
  431.  do
  432.    {
  433.     str=read_line_or_timeout(server_ctrl,str);
  434. #if DEBUG_FTP
  435.     PrintMessage(Debug,"FTP: sent 'PASV'; got: %s",str);
  436. #endif
  437.    }
  438.  while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  439.  
  440.  if(!str || atoi(str)!=227)
  441.    {
  442.     if(str)
  443.       {
  444.        char *p=str+strlen(str)-1;
  445.        while(*p=='\n' || *p=='\r') *p--=0;
  446.        msg=PrintMessage(Warning,"Got '%s' message after sending 'PASV' command",str);
  447.       }
  448.     else
  449.        msg=PrintMessage(Warning,"No reply from FTP server to 'PASV' command; timed out?");
  450.     return(msg);
  451.    }
  452.  
  453.  if((host=strchr(str,',')))
  454.    {
  455.     while(isdigit(*--host));
  456.     host++;
  457.    }
  458.  
  459.  if(!host || sscanf(host,"%*d,%*d,%*d,%*d%n,%d,%d",&l,&port_h,&port_l)!=2)
  460.    {
  461.     char *p=str+strlen(str)-1;
  462.     while(*p=='\n' || *p=='\r') *p--=0;
  463.     msg=PrintMessage(Warning,"Got '%s' message after sending 'PASV' command, cannot parse.",str);
  464.     return(msg);
  465.    }
  466.  
  467.  host[l]=0;
  468.  for(;l>0;l--)
  469.     if(host[l]==',')
  470.        host[l]='.';
  471.  
  472.  server_data=OpenClientSocket(host,port_l+256*port_h);
  473.  init_buffer(server_data);
  474.  
  475.  if(server_data==-1)
  476.    {
  477.     msg=PrintMessage(Warning,"Cannot open the FTP data connection [%!s].");
  478.     return(msg);
  479.    }
  480.  
  481.  /* Make the request */
  482.  
  483.  if(file)
  484.    {
  485.     if(write_formatted(server_ctrl,"RETR %s\r\n",file)==-1)
  486.        {
  487.         msg=PrintMessage(Warning,"Failed to write 'RETR' command to remote FTP host [%!s].");
  488.         return(msg);
  489.        }
  490.  
  491.     do
  492.       {
  493.        str=read_line_or_timeout(server_ctrl,str);
  494. #if DEBUG_FTP
  495.        PrintMessage(Debug,"FTP: sent 'RETR %s'; got: %s",file,str);
  496. #endif
  497.       }
  498.     while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  499.  
  500.     mimetype=WhatMIMEType(file);
  501.    }
  502.  else
  503.    {
  504.     if(write_string(server_ctrl,"LIST\r\n")==-1)
  505.       {
  506.        msg=PrintMessage(Warning,"Failed to write 'LIST' command to remote FTP host [%!s].");
  507.        return(msg);
  508.       }
  509.  
  510.     do
  511.       {
  512.        str=read_line_or_timeout(server_ctrl,str);
  513. #if DEBUG_FTP
  514.        PrintMessage(Debug,"FTP: sent 'LIST'; got: %s",str);
  515. #endif
  516.       }
  517.     while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  518.  
  519.     mimetype="text/html";
  520.    }
  521.  
  522.  if(str && (atoi(str)==150 || atoi(str)==125))
  523.     *str='2',*(str+1)='0',*(str+2)='0';
  524.  else
  525.    {
  526.     if(str)
  527.       {
  528.        char *p=str+strlen(str)-1;
  529.        while(*p=='\n' || *p=='\r') *p--=0;
  530.        msg=PrintMessage(Warning,"Got '%s' message after sending '%s' command to FTP server.",str,file?"RETR":"LIST");
  531.       }
  532.     else
  533.        msg=PrintMessage(Warning,"No reply from FTP server to '%s' command; timed out?",file?"RETR":"LIST");
  534.     return(msg);
  535.    }
  536.  
  537.  /* Prepare the HTTP header. */
  538.  
  539.  bufferhead=(char*)malloc(strlen(str)+strlen(mimetype)+32);
  540.  strcpy(bufferhead,"HTTP/1.0 ");
  541.  strcat(bufferhead,str);
  542.  strcat(bufferhead,"Content-Type: ");
  543.  strcat(bufferhead,mimetype);
  544.  strcat(bufferhead,"\r\n");
  545.  strcat(bufferhead,"\r\n");
  546.  
  547.  buffer=NULL;
  548.  buffertail=NULL;
  549.  
  550.  if(!file)
  551.    {
  552.     char *msg=
  553.     "<HTML>\n"
  554.     "<HEAD>\n"
  555.     "<TITLE>Index of %s</TITLE>\n"
  556.     "<BASE HREF=\"%s\">\n"
  557.     "</HEAD>\n"
  558.     "<BODY>\n"
  559.     "<h1>Index of %s</h1>\n"
  560.     "<pre>\n";
  561.     char *msgtail=
  562.     "</pre>\n"
  563.     "</BODY>\n"
  564.     "</HTML>\n";
  565.  
  566.     char *path=UrlDecode(Url->name,0);
  567.  
  568.     if(msg_reply)
  569.       {
  570.        char *old_msg_reply=msg_reply;
  571.        msg_reply=HTMLString(msg_reply);
  572.        free(old_msg_reply);
  573.       }
  574.  
  575.     buffer=(char*)malloc((msg_reply?strlen(msg_reply)+8:0)+strlen(msg)+3*strlen(path)+8);
  576.     sprintf(buffer,msg,path,path,path);
  577.  
  578.     if(msg_reply)
  579.       {
  580.        strcat(buffer,msg_reply);
  581.        strcat(buffer,"</pre>\n"
  582.                      "<pre>\n");
  583.       }
  584.  
  585.     buffertail=msgtail;
  586.  
  587.     free(path);
  588.    }
  589.  
  590. near_end:
  591.  
  592.  if(msg_reply)
  593.     free(msg_reply);
  594.  
  595.  nbufferhead=strlen(bufferhead); nreadhead=0;
  596.  nbuffer=buffer?strlen(buffer):0; nread=0;
  597.  nbuffertail=buffertail?strlen(buffertail):0; nreadtail=0;
  598.  
  599.  free(str);
  600.  free(path);
  601.  
  602.  return(msg);
  603. }
  604.  
  605.  
  606. /*++++++++++++++++++++++++++++++++++++++
  607.   Read a line from the header of the reply for the URL.
  608.  
  609.   char *FTP_ReadHead Returns the next line of data, NULL on EOF.
  610.  
  611.   char *line The previous line read.
  612.   ++++++++++++++++++++++++++++++++++++++*/
  613.  
  614. char *FTP_ReadHead(char *line)
  615. {
  616.  char *l=line;
  617.  int m;
  618.  
  619.  /* Take a simple route if it is proxied. */
  620.  
  621.  if(proxy)
  622.     return(read_line_or_timeout(server_ctrl,line));
  623.  
  624.  /* Else send the header. */
  625.  
  626.  if(nreadhead==nbufferhead)
  627.     return(NULL);
  628.  
  629.  for(m=nreadhead;m<nbufferhead;m++)
  630.     if(bufferhead[m]=='\n')
  631.        break;
  632.  
  633.  m++;
  634.  l=(char*)realloc((void*)l,m-nreadhead+1);
  635.  strncpy(l,&bufferhead[nreadhead],m-nreadhead);
  636.  l[m-nreadhead]=0;
  637.  
  638.  nreadhead=m;
  639.  
  640.  return(l);
  641. }
  642.  
  643.  
  644. /*++++++++++++++++++++++++++++++++++++++
  645.   Read bytes from the body of the reply for the URL.
  646.  
  647.   int FTP_ReadBody Returns the number of bytes read on success, -1 on error.
  648.  
  649.   char *s A string to fill in with the information.
  650.  
  651.   int n The number of bytes to read.
  652.   ++++++++++++++++++++++++++++++++++++++*/
  653.  
  654. int FTP_ReadBody(char *s,int n)
  655. {
  656.  int m=0;
  657.  
  658.  /* Take a simple route if it is proxied. */
  659.  
  660.  if(proxy)
  661.     return(read_data_or_timeout(server_ctrl,s,n));
  662.  
  663.  /* Else send the data then the tail. */
  664.  
  665.  if(server_data==-1)            /* Redirection */
  666.    {
  667.     for(;nread<nbuffer && m<n;nread++,m++)
  668.        s[m]=buffer[nread];
  669.  
  670.     for(;nreadtail<nbuffertail && m<n;nreadtail++,m++)
  671.        s[m]=buffertail[nreadtail];
  672.    }
  673.  else if(!buffer && !buffertail) /* File not dir entry. */
  674.     m=read_data_or_timeout(server_data,s,n);
  675.  else if(buffer && buffertail)  /* Middle of dir entry */
  676.    {
  677.     for(;nread<nbuffer && m<n;nread++,m++)
  678.        s[m]=buffer[nread];
  679.  
  680.     if(nread==nbuffer)
  681.       {
  682.        buffer=htmlise_dir_entry(buffer);
  683.        if(buffer)
  684.           nbuffer=strlen(buffer),nread=0;
  685.       }
  686.  
  687.     if(!buffer)
  688.        for(;nreadtail<nbuffertail && m<n;nreadtail++,m++)
  689.           s[m]=buffertail[nreadtail];
  690.    }
  691.  else if(!buffer && buffertail) /* End of dir entry. */
  692.    {
  693.     for(;nreadtail<nbuffertail && m<n;nreadtail++,m++)
  694.        s[m]=buffertail[nreadtail];
  695.    }
  696.  
  697.  /* Clear up at the very end. */
  698.  
  699.  if(m==0)
  700.     if(buffer)
  701.        free(buffer);
  702.  
  703.  return(m);
  704. }
  705.  
  706.  
  707. /*++++++++++++++++++++++++++++++++++++++
  708.   Close a connection opened using FTP.
  709.  
  710.   int FTP_Close Return 0 on success, -1 on error.
  711.   ++++++++++++++++++++++++++++++++++++++*/
  712.  
  713. int FTP_Close(void)
  714. {
  715.  int err=0;
  716.  char *str=NULL;
  717.  
  718.  /* Take a simple route if it is proxied. */
  719.  
  720.  if(proxy)
  721.     return(CloseSocket(server_ctrl));
  722.  
  723.  /* Else say goodbye and close all of the sockets, */
  724.  
  725.  write_string(server_ctrl,"QUIT\r\n");
  726.  
  727.  do
  728.    {
  729.     str=read_line_or_timeout(server_ctrl,str);
  730. #if DEBUG_FTP
  731.     PrintMessage(Debug,"FTP: sent 'QUIT'; got: %s",str);
  732. #endif
  733.    }
  734.  while(str && (!isdigit(str[0]) || !isdigit(str[1]) || !isdigit(str[2]) || str[3]!=' '));
  735.  
  736.  /* Close the sockets */
  737.  
  738.  err=CloseSocket(server_ctrl);
  739.  
  740.  if(server_data!=-1)
  741.     CloseSocket(server_data);
  742.  
  743.  if(bufferhead)
  744.     free(bufferhead);
  745.  
  746.  return(err);
  747. }
  748.  
  749.  
  750. /*++++++++++++++++++++++++++++++++++++++
  751.   Convert a line from the ftp server dir listing into a pretty listing.
  752.  
  753.   char *htmlise_dir_entry Returns the next line.
  754.  
  755.   char *line The previous line.
  756.   ++++++++++++++++++++++++++++++++++++++*/
  757.  
  758. static char *htmlise_dir_entry(char *line)
  759. {
  760.  int i,isdir=0;
  761.  char *q,*p[16],*l,*file=NULL,*link=NULL;
  762.  
  763.  l=read_line_or_timeout(server_data,line);
  764.  
  765.  if(!l)
  766.     return(l);
  767.  
  768.  for(q=l,i=0;*q && i<16;i++)
  769.    {
  770.     while(*q==' ' || *q=='\t')
  771.        q++;
  772.     if(*q=='\n' || *q=='\r')
  773.        break;
  774.     if(*q)
  775.        p[i]=q;
  776.     while(*q && *q!=' ' && *q!='\t' && *q!='\r' && *q!='\n')
  777.        q++;
  778.    }
  779.  
  780.  if((*p[0]=='-' || *p[0]=='d' || *p[0]=='l') && i>=8) /* A UNIX 'ls -l' listing. */
  781.    {
  782.     if(i==8)
  783.       {file=p[7]; link=file;}
  784.     else if(i==10 && (*p[8]=='-' && *(p[8]+1)=='>'))
  785.       {file=p[7]; link=p[9];}
  786.     else if(i==9)
  787.       {file=p[8]; link=file;}
  788.     else if(i==11 && (*p[9]=='-' && *(p[9]+1)=='>'))
  789.       {file=p[8]; link=p[10];}
  790.  
  791.     if(*p[0]=='d')
  792.        isdir=1;
  793.    }
  794.  
  795.  if(file)
  796.    {
  797.     char *endf,endfc,*endl,endlc,*ll,*urlenc;
  798.  
  799.     endf=file;
  800.     while(*endf && *endf!=' ' && *endf!='\t' && *endf!='\r' && *endf!='\n')
  801.        endf++;
  802.     endfc=*endf;
  803.  
  804.     endl=link;
  805.     while(*endl && *endl!=' ' && *endl!='\t' && *endl!='\r' && *endl!='\n')
  806.        endl++;
  807.     endlc=*endl;
  808.  
  809.     *endl=0;
  810.     urlenc=UrlEncode(link);
  811.     *endl=endlc;
  812.  
  813.     ll=(char*)malloc(strlen(l)+strlen(urlenc)+24);
  814.  
  815.     strncpy(ll,l,file-l);
  816.     strcpy(ll+(file-l),"<a href=\"");
  817.     strcat(ll,urlenc);
  818.     if(isdir && *(endl-1)!='/')
  819.        strcat(ll,"/");
  820.     strcat(ll,"\">");
  821.     *endf=0;
  822.     strcat(ll,file);
  823.     *endf=endfc;
  824.     strcat(ll,"</a>");
  825.     strcat(ll,endf);
  826.  
  827.     free(urlenc);
  828.     if(l)
  829.        free(l);
  830.     l=ll;
  831.    }
  832.  
  833.  return(l);
  834. }
  835.