home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus Leser 15 / Amiga Plus Leser CD 15.iso / Tools / Development / MosaicSRC / libwww2 / HTTP.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-03-13  |  20.6 KB  |  726 lines

  1. /*    HyperText Tranfer Protocol    - Client implementation        HTTP.c
  2. **    ==========================
  3. */
  4.  
  5. #include "HTTP.h"
  6.  
  7. #define HTTP_VERSION    "HTTP/1.0"
  8.  
  9. #define INIT_LINE_SIZE        1024    /* Start with line buffer this big */
  10. #define LINE_EXTEND_THRESH    256    /* Minimum read size */
  11. #define VERSION_LENGTH         20    /* for returned protocol version */
  12.  
  13. #include "HTParse.h"
  14. #include "HTUtils.h"
  15. #include "tcp.h"
  16. #include "HTTCP.h"
  17. #include "HTFormat.h"
  18. #include "HTFile.h"
  19. #include <ctype.h>
  20. #include "HTAlert.h"
  21. #include "HTMIME.h"
  22. #include "HTML.h"
  23. #include "HTInit.h"
  24. #include "HTAABrow.h"
  25.  
  26. /* #define TRACE 1 */
  27.  
  28. struct _HTStream 
  29. {
  30.   HTStreamClass * isa;
  31. };
  32.  
  33. extern char * HTAppName;    /* Application name: please supply */
  34. extern char * HTAppVersion;    /* Application version: please supply */
  35.  
  36. /* Variables that control whether we do a POST or a GET,
  37.    and if a POST, what and how we POST. */
  38. int do_post = 0;
  39. char *post_content_type = NULL;
  40. char *post_data = NULL;
  41. extern BOOL using_gateway;    /* are we using an HTTP gateway? */
  42. extern BOOL using_proxy;      /* are we using an HTTP proxy gateway? */
  43.  
  44.  
  45. /*        Load Document from HTTP Server            HTLoadHTTP()
  46. **        ==============================
  47. **
  48. **    Given a hypertext address, this routine loads a document.
  49. **
  50. **
  51. ** On entry,
  52. **    arg    is the hypertext reference of the article to be loaded.
  53. **
  54. ** On exit,
  55. **    returns    >=0    If no error, a good socket number
  56. **        <0    Error.
  57. **
  58. **    The socket must be closed by the caller after the document has been
  59. **    read.
  60. **
  61. */
  62. PUBLIC int HTLoadHTTP ARGS4 (
  63.     char *,         arg,
  64.     HTParentAnchor *,    anAnchor,
  65.     HTFormat,        format_out,
  66.     HTStream*,        sink)
  67. {
  68.   int s;                /* Socket number for returned data */
  69.   char *command;            /* The whole command */
  70.   char *eol;            /* End of line if found */
  71.   char *start_of_data;        /* Start of body of reply */
  72.   int status;                /* tcp return */
  73.   int bytes_already_read;
  74.   char crlf[3];            /* A CR LF equivalent string */
  75.   HTStream *target;        /* Unconverted data */
  76.   HTFormat format_in;            /* Format arriving in the message */
  77.   
  78.   BOOL had_header;        /* Have we had at least one header? */
  79.   char *line_buffer;
  80.   char *line_kept_clean;
  81.   BOOL extensions;        /* Assume good HTTP server */
  82.   int compressed;
  83.   char line[256];
  84.   
  85.   int length, doing_redirect, rv;
  86.   int already_retrying = 0;
  87.   int return_nothing;
  88.  
  89. #ifdef PEM_AUTH
  90.   BOOL doing_pem;
  91.   char *body;
  92.   int body_size;
  93. #endif /* PEM_AUTH */
  94.   
  95.   if (!arg)
  96.     {
  97.       status = -3;
  98.       HTProgress ("Bad request.");
  99.       goto done;
  100.     }
  101.   if (!*arg) 
  102.     {
  103.       status = -2;
  104.       HTProgress ("Bad request.");
  105.       goto done;
  106.     }
  107.   
  108.   sprintf(crlf, "%c%c", CR, LF);
  109.  
  110.   /* At this point, we're talking HTTP/1.0. */
  111.   extensions = YES;
  112.  
  113.  try_again:
  114.   /* All initializations are moved down here from up above,
  115.      so we can start over here... */
  116.   eol = 0;
  117.   bytes_already_read = 0;
  118.   had_header = NO;
  119.   length = 0;
  120.   doing_redirect = 0;
  121.   compressed = 0;
  122.   target = NULL;
  123.   line_buffer = NULL;
  124.   line_kept_clean = NULL;
  125.   return_nothing = 0;
  126. #ifdef PEM_AUTH
  127.   doing_pem = NO;
  128. #endif /* PEM_AUTH */
  129.  
  130.   status = HTDoConnect (arg, "HTTP", TCP_PORT, &s);
  131.   if (status == HT_INTERRUPTED)
  132.     {
  133.       /* Interrupt cleanly. */
  134.       if (TRACE)
  135.         fprintf (stderr,
  136.                  "HTTP: Interrupted on connect; recovering cleanly.\n");
  137.       HTProgress ("Connection interrupted.");
  138.       /* status already == HT_INTERRUPTED */
  139.       goto done;
  140.     }
  141.   if (status < 0) 
  142.     {
  143.       if (TRACE) 
  144.         fprintf(stderr, 
  145.                 "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno);
  146.       HTProgress ("Unable to connect to remote host.");
  147.       status = HT_NO_DATA;
  148.       goto done;
  149.     }
  150.   
  151.   /*    Ask that node for the document,
  152.    **    omitting the host name & anchor
  153.    */        
  154.   {
  155.     char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
  156.     command = malloc(5 + strlen(p1)+ 2 + 31);
  157.  
  158.     if (do_post)
  159.       strcpy(command, "POST ");
  160.     else
  161.       strcpy(command, "GET ");
  162.  
  163.     /*
  164.      * For a gateway, the beginning '/' on the request must
  165.      * be stripped before appending to the gateway address.
  166.      */
  167.     if ((using_gateway)||(using_proxy))
  168.         strcat(command, p1+1);
  169.     else
  170.         strcat(command, p1);
  171.     free(p1);
  172.   }
  173.   if (extensions) 
  174.     {
  175.       strcat(command, " ");
  176.       strcat(command, HTTP_VERSION);
  177.     }
  178.   
  179.   strcat(command, crlf);    /* CR LF, as in rfc 977 */
  180.   
  181.   if (extensions) 
  182.     {
  183.       int n, i;
  184.       
  185. #if    0
  186.       if (!HTPresentations) HTFormatInit();
  187.       n = HTList_count(HTPresentations);
  188.       
  189.       for(i=0; i<n; i++) 
  190.         {
  191.           HTPresentation * pres = HTList_objectAt(HTPresentations, i);
  192.           if (pres->rep_out == WWW_PRESENT) 
  193.             {
  194.               sprintf(line, "Accept: %s%c%c",
  195.                       HTAtom_name(pres->rep), CR, LF);
  196.               StrAllocCat(command, line);
  197.             }
  198.         }
  199.       
  200. #endif
  201.       sprintf(line, "User-Agent:  %s/%s  libwww/%s%c%c",
  202.               HTAppName ? HTAppName : "unknown",
  203.               HTAppVersion ? HTAppVersion : "0.0",
  204.               HTLibraryVersion, CR, LF);
  205.       StrAllocCat(command, line);
  206.       
  207.       {
  208.         char *docname;
  209.         char *hostname;
  210.         char *colon;
  211.         int portnumber;
  212.         char *auth;
  213.         
  214.         docname = HTParse(arg, "", PARSE_PATH);
  215.         hostname = HTParse(arg, "", PARSE_HOST);
  216.         if (hostname &&
  217.             NULL != (colon = strchr(hostname, ':'))) 
  218.           {
  219.             *(colon++) = '\0';    /* Chop off port number */
  220.             portnumber = atoi(colon);
  221.           }
  222.         else portnumber = 80;
  223.         
  224.         if (NULL!=(auth=HTAA_composeAuth(hostname, portnumber, docname))) 
  225.           {
  226.             sprintf(line, "%s%c%c", auth, CR, LF);
  227.             StrAllocCat(command, line);
  228.           }
  229.         if (TRACE) 
  230.           {
  231.             if (auth)
  232.               fprintf(stderr, "HTTP: Sending authorization: %s\n", auth);
  233.             else
  234.               fprintf(stderr, "HTTP: Not sending authorization (yet)\n");
  235.           }
  236.         FREE(hostname);
  237.         FREE(docname);
  238.       }
  239.     }
  240.  
  241.   if (do_post)
  242.     {
  243.       if (TRACE)
  244.         fprintf (stderr, "HTTP: Doing post, content-type '%s'\n",
  245.                  post_content_type);
  246.       sprintf (line, "Content-type: %s%c%c",
  247.                post_content_type ? post_content_type : "lose", CR, LF);
  248.       StrAllocCat(command, line);
  249.       {
  250.         int content_length;
  251.         if (!post_data)
  252.           content_length = 4; /* 4 == "lose" :-) */
  253.         else
  254.           content_length = strlen (post_data);
  255.         sprintf (line, "Content-length: %d%c%c",
  256.                  content_length, CR, LF);
  257.         StrAllocCat(command, line);
  258.       }
  259.       
  260.       StrAllocCat(command, crlf);    /* Blank line means "end" */
  261.       
  262.       if (post_data)
  263.         StrAllocCat(command, post_data);
  264.       else
  265.         StrAllocCat(command, "lose");
  266.     }
  267.  
  268.   StrAllocCat(command, crlf);    /* Blank line means "end" */
  269.  
  270. #ifdef PEM_AUTH
  271. /*
  272.  * HTAA_makecommand will check if we're doing PEM authentication, and if
  273.  * so, will return a new command which is actually the command which was
  274.  * just built, encrypted, and placed inside a dummy HTTP request. Body
  275.  * is kept separate from the command because in the case of PGP data, the
  276.  * message body can be binary.
  277.  */
  278.  
  279.   body = NULL;
  280.   body_size = -1;
  281.   command = HTAA_makecommand(command,&body,&body_size);
  282. #endif /* PEM_AUTH */
  283.   
  284.   if (TRACE)
  285.     fprintf (stderr, "Writing:\n%s----------------------------------\n",
  286.              command);
  287.   
  288.   HTProgress ("Sending HTTP request.");
  289.  
  290.   status = NETWRITE(s, command, (int)strlen(command));
  291. #ifdef PEM_AUTH
  292.   if(body) {
  293.       status = NETWRITE(s, body, body_size);
  294.       free(body);
  295.   }
  296. #endif /* PEM_AUTH */
  297.   free (command);
  298.   if (status <= 0) 
  299.     {
  300.       if (status == 0)
  301.         {
  302.           if (TRACE)
  303.             fprintf (stderr, "HTTP: Got status 0 in initial write\n");
  304.           /* Do nothing. */
  305.         }
  306.       else if 
  307.         ((errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) &&
  308.          !already_retrying &&
  309.          /* Don't retry if we're posting. */ !do_post)
  310.           {
  311.             /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
  312.             if (TRACE)
  313.               fprintf 
  314.                 (stderr, 
  315.                  "HTTP: BONZO ON WRITE Trying again with HTTP0 request.\n");
  316.             HTProgress ("Retrying as HTTP0 request.");
  317.             NETCLOSE(s);
  318.             extensions = NO;
  319.             already_retrying = 1;
  320.             goto try_again;
  321.           }
  322.       else
  323.         {
  324.           if (TRACE)
  325.             fprintf (stderr, "HTTP: Hit unexpected network WRITE error; aborting connection.\n");
  326.           NETCLOSE (s);
  327.           status = -1;
  328.           HTProgress ("Unexpected network write error; connection aborted.");
  329.           goto done;
  330.         }
  331.     }
  332.   
  333.   if (TRACE)
  334.     fprintf (stderr, "HTTP: WRITE delivered OK\n");
  335.   HTProgress ("Done sending HTTP request; waiting for response.");
  336.  
  337.   /*    Read the first line of the response
  338.    **    -----------------------------------
  339.    */
  340.  
  341. #ifdef PEM_AUTH
  342. /* 
  343.  * reparse: We got an encapsulated response from the server, and have 
  344.  * decrypted it. Now we restart the whole process with the decrypted 
  345.  * response.
  346.  */
  347.  reparse:
  348.   length=0;
  349.   bytes_already_read=0;
  350.   eol = 0;
  351. #endif /* PEM_AUTH */ 
  352.   {
  353.     /* Get numeric status etc */
  354.     BOOL end_of_file = NO;
  355.     HTAtom * encoding = HTAtom_for("8bit");
  356.     int buffer_length = INIT_LINE_SIZE;
  357.     
  358.     line_buffer = (char *) malloc(buffer_length * sizeof(char));
  359.     
  360.     do 
  361.       {    /* Loop to read in the first line */
  362.         /* Extend line buffer if necessary for those crazy WAIS URLs ;-) */
  363.         if (buffer_length - length < LINE_EXTEND_THRESH) 
  364.           {
  365.             buffer_length = buffer_length + buffer_length;
  366.             line_buffer = 
  367.               (char *) realloc(line_buffer, buffer_length * sizeof(char));
  368.           }
  369.         if (TRACE)
  370.           fprintf (stderr, "HTTP: Trying to read %d\n",
  371.                    buffer_length - length - 1);
  372.         status = NETREAD(s, line_buffer + length,
  373.                          buffer_length - length - 1);
  374.         if (TRACE)
  375.           fprintf (stderr, "HTTP: Read %d\n", status);
  376.         if (status <= 0) 
  377.           {
  378.             /* Retry if we get nothing back too; 
  379.                bomb out if we get nothing twice. */
  380.             if (status == HT_INTERRUPTED)
  381.               {
  382.                 if (TRACE)
  383.                   fprintf (stderr, "HTTP: Interrupted initial read.\n");
  384.                 HTProgress ("Connection interrupted.");
  385.                 status = HT_INTERRUPTED;
  386.         NETCLOSE (s);
  387.                 goto clean_up;
  388.               }
  389.             else if 
  390.               (status < 0 &&
  391.                (errno == ENOTCONN || errno == ECONNRESET || errno == EPIPE) &&
  392.                !already_retrying &&
  393.                !do_post)
  394.               {
  395.                 /* Arrrrgh, HTTP 0/1 compability problem, maybe. */
  396.                 if (TRACE)
  397.                   fprintf (stderr, "HTTP: BONZO Trying again with HTTP0 request.\n");
  398.                 NETCLOSE(s);
  399.                 if (line_buffer) 
  400.                   free(line_buffer);
  401.                 if (line_kept_clean) 
  402.                   free(line_kept_clean);
  403.                 
  404.                 extensions = NO;
  405.                 already_retrying = 1;
  406.                 HTProgress ("Retrying as HTTP0 request.");
  407.                 goto try_again;
  408.               }
  409.             else
  410.               {
  411.                 if (TRACE)
  412.                   fprintf (stderr, "HTTP: Hit unexpected network read error; aborting connection; status %d.\n", status);
  413.                 HTProgress 
  414.                   ("Unexpected network read error; connection aborted.");
  415.  
  416.                 NETCLOSE (s);
  417.                 status = -1;
  418.                 goto clean_up;
  419.               }
  420.           }
  421.  
  422.         bytes_already_read += status;
  423.         {
  424.           char line[256];
  425.           sprintf (line, "Read %d bytes of data.", bytes_already_read);
  426.           HTProgress (line);
  427.         }
  428.         
  429.         if (status == 0) 
  430.           {
  431.             end_of_file = YES;
  432.             break;
  433.           }
  434.         line_buffer[length+status] = 0;
  435.         
  436.         if (line_buffer)
  437.           {
  438.             if (line_kept_clean)
  439.               free (line_kept_clean);
  440.             line_kept_clean = (char *)malloc (buffer_length * sizeof (char));
  441.             bcopy (line_buffer, line_kept_clean, buffer_length);
  442.           }
  443.         
  444.         eol = strchr(line_buffer + length, LF);
  445.         /* Do we *really* want to do this? */
  446.         if (eol && eol != line_buffer && *(eol-1) == CR) 
  447.           *(eol-1) = ' '; 
  448.         
  449.         length = length + status;
  450.  
  451.         /* Do we really want to do *this*? */
  452.         if (eol) 
  453.           *eol = 0;        /* Terminate the line */
  454.       }
  455.     /* All we need is the first line of the response.  If it's a HTTP/1.0
  456.        response, then the first line will be absurdly short and therefore
  457.        we can safely gate the number of bytes read through this code
  458.        (as opposed to below) to ~1000. */
  459.     /* Well, let's try 100. */
  460.     while (!eol && !end_of_file && bytes_already_read < 100);
  461.   } /* Scope of loop variables */
  462.     
  463.     
  464.   /*    We now have a terminated unfolded line. Parse it.
  465.    **    -------------------------------------------------
  466.    */
  467.   if (TRACE)
  468.     fprintf(stderr, "HTTP: Rx: %s\n", line_buffer);
  469.   
  470.   {
  471.     int fields;
  472.     char server_version[VERSION_LENGTH+1];
  473.     int server_status;
  474.  
  475.     server_version[0] = 0;
  476.     
  477.     fields = sscanf(line_buffer, "%20s %d",
  478.                     server_version,
  479.                     &server_status);
  480.     
  481.     if (TRACE)
  482.       fprintf (stderr, "HTTP: Scanned %d fields from line_buffer\n", fields);
  483.     if (TRACE)
  484.       fprintf (stderr, "HTTP: line_buffer '%s'\n", line_buffer);
  485.     
  486.     /* Rule out HTTP/1.0 reply as best we can. */
  487.     if (fields < 2 || !server_version[0] || server_version[0] != 'H' ||
  488.         server_version[1] != 'T' || server_version[2] != 'T' ||
  489.         server_version[3] != 'P' || server_version[4] != '/' ||
  490.         server_version[6] != '.') 
  491.       {    
  492.         /* HTTP0 reply */
  493.         HTAtom * encoding;
  494.  
  495.         if (TRACE)
  496.           fprintf (stderr, "--- Talking HTTP0.\n");
  497.         
  498.         format_in = HTFileFormat(arg, &encoding, WWW_HTML, &compressed);
  499.         start_of_data = line_kept_clean;
  500.       } 
  501.     else 
  502.       {
  503.         /* Decode full HTTP response */
  504.         format_in = HTAtom_for("www/mime");
  505.         /* We set start_of_data to "" when !eol here because there
  506.            will be a put_block done below; we do *not* use the value
  507.            of start_of_data (as a pointer) in the computation of
  508.            length or anything else in this situation. */
  509.         start_of_data = eol ? eol + 1 : "";
  510.         length = eol ? length - (start_of_data - line_buffer) : 0;
  511.         
  512.         if (TRACE)
  513.           fprintf (stderr, "--- Talking HTTP1.\n");
  514.         
  515.         switch (server_status / 100) 
  516.           {
  517.           case 3:        /* Various forms of redirection */
  518.             /* We now support this in the parser, at least. */
  519.             doing_redirect = 1;
  520.             break;
  521.             
  522.           case 4:        /* "I think I goofed" */
  523.             switch (server_status) 
  524.               {
  525.               case 403:
  526.                 /* 403 is "forbidden"; display returned text. */
  527.                 break;
  528.  
  529.               case 401:
  530.                 /* length -= start_of_data - text_buffer; */
  531.                 if (HTAA_shouldRetryWithAuth(start_of_data, length, s)) 
  532.                   {
  533.                     (void)NETCLOSE(s);
  534.                     if (line_buffer) 
  535.                       free(line_buffer);
  536.                     if (line_kept_clean) 
  537.                       free(line_kept_clean);
  538.  
  539.                     if (TRACE) 
  540.                       fprintf(stderr, "%s %d %s\n",
  541.                               "HTTP: close socket", s,
  542.                               "to retry with Access Authorization");
  543.                     
  544.                     HTProgress ("Retrying with access authorization information.");
  545.                     goto try_again;
  546.                     break;
  547.                   }
  548.                 else 
  549.                   {
  550.                     /* Fall through. */
  551.                   }
  552.  
  553.               default:
  554.                 break;
  555.               } /* case 4 switch */
  556.             break;
  557.  
  558.           case 5:        /* I think you goofed */
  559.             break;
  560.             
  561.           case 2:        /* Good: Got MIME object */
  562.             switch (server_status)
  563.               {
  564.               case 204:
  565.                 return_nothing = 1;
  566.                 format_in = HTAtom_for("text/html");
  567.                 break;
  568.               default:
  569. #ifdef PEM_AUTH
  570. /*
  571.  * HTAA_switchPEMresponse will return us a new file descriptor if we should
  572.  * re-parse the response, will return -1 if some error occured, and will 
  573.  * return -2 if there was no PEM going on at the time.
  574.  */
  575.                 {
  576.                     int s2;
  577.                     if(doing_pem == NO) {
  578.                         if((s2 = HTAA_switchPEMresponse(start_of_data, length, s)) == -1) {
  579.                             HTProgress("An error occured while decrypting.");
  580.                             status = -1;
  581.                 NETCLOSE (s);
  582.                             goto clean_up;
  583.                         }
  584.  
  585.             if (s2 == -3)
  586.             {
  587.                 HTAA_ClearAuth();
  588.                 (void)NETCLOSE(s);
  589.                 if (line_buffer) 
  590.                   free(line_buffer);
  591.                 if (line_kept_clean) 
  592.                   free(line_kept_clean);
  593.  
  594.                 if (TRACE) 
  595.                   fprintf(stderr, "%s %d %s\n",
  596.                       "HTTP: close socket", s,
  597.                       "to retry without Access Authorization");
  598.                     
  599.                 HTProgress ("Retrying without access authorization information.");
  600.                 goto try_again;
  601.             }
  602.  
  603.                         if(s2 != -2) {
  604.                             HTProgress("Got encrypted response, decrypting.");
  605.                             doing_pem = YES;
  606.                             s = s2;
  607.                             goto reparse;
  608.                         }
  609.                     }
  610.                 }
  611. #endif /* PEM_AUTH */
  612.                 break;
  613.               }
  614.             break;
  615.             
  616.           default:        /* bad number */
  617.             HTAlert("Unknown status reply from server!");
  618.             break;
  619.           } /* Switch on server_status/100 */
  620.         
  621.       }    /* Full HTTP reply */
  622.   } /* scope of fields */
  623.  
  624.   /* Set up the stream stack to handle the body of the message */
  625.   target = HTStreamStack(format_in,
  626.                          format_out,
  627.                          compressed,
  628.                          sink, anAnchor);
  629.   
  630.   if (!target) 
  631.     {
  632.       char buffer[1024];    /* @@@@@@@@ */
  633.       sprintf(buffer, "Sorry, no known way of converting %s to %s.",
  634.               HTAtom_name(format_in), HTAtom_name(format_out));
  635.       HTProgress (buffer);
  636.       status = -1;
  637.       NETCLOSE (s);
  638.       goto clean_up;
  639.     }
  640.  
  641.   if (!return_nothing)
  642.     {
  643.       if (TRACE)
  644.         fprintf (stderr, "HTTP: Doing put_block, '%s'\n", start_of_data);
  645.       /* Recycle the first chunk of data, in all cases. */
  646.       (*target->isa->put_block)(target, start_of_data, length);
  647.       
  648.       /* Go pull the bulk of the data down. */
  649.       rv = HTCopy(s, target, bytes_already_read);
  650.       if (rv == -1)
  651.         {
  652.           (*target->isa->handle_interrupt) (target);
  653.           status = HT_INTERRUPTED;
  654.       NETCLOSE (s);
  655.           goto clean_up;
  656.         }
  657.       if (rv == -2 && !already_retrying && !do_post)
  658.         {
  659.           /* Aw hell. */
  660.           if (TRACE)
  661.             fprintf (stderr, "HTTP: Trying again with HTTP0 request.\n");
  662.           /* May as well consider it an interrupt -- right? */
  663.           (*target->isa->handle_interrupt) (target);
  664.           NETCLOSE(s);
  665.           if (line_buffer) 
  666.             free(line_buffer);
  667.           if (line_kept_clean) 
  668.             free(line_kept_clean);
  669.           
  670.           extensions = NO;
  671.           already_retrying = 1;
  672.           HTProgress ("Retrying as HTTP0 request.");
  673.           goto try_again;
  674.         }
  675.     }
  676.   else
  677.     {
  678.       /* return_nothing is high. */
  679.       (*target->isa->put_string) (target, "<mosaic-access-override>\n");
  680.       HTProgress ("And silence filled the night.");
  681.     }
  682.  
  683.   (*target->isa->end_document)(target);
  684.  
  685. #ifdef PEM_AUTH
  686.   /* If we're processing a PEM decrypted response, do extra cleanup */
  687.   if(!HTAA_PEMclose(s))
  688. #endif /* PEM_AUTH */
  689.   /* Close socket before doing free. */
  690.   NETCLOSE(s);
  691.   (*target->isa->free)(target);
  692.  
  693.   if (doing_redirect)
  694.     {
  695.       /* OK, now we've got the redirection URL temporarily stored
  696.          in external variable redirecting_url, exported from HTMIME.c,
  697.          since there's no straightforward way to do this in the library
  698.          currently.  Do the right thing. */
  699.       status = HT_REDIRECTING;
  700.     }
  701.   else
  702.     {
  703.       status = HT_LOADED;
  704.     }
  705.  
  706.   /*    Clean up
  707.    */
  708.   
  709.  clean_up: 
  710.   if (line_buffer) 
  711.     free(line_buffer);
  712.   if (line_kept_clean) 
  713.     free(line_kept_clean);
  714.  
  715.  done:
  716.   /* Clear out on exit, just in case. */
  717.   do_post = 0;
  718.   return status;
  719. }
  720.  
  721.  
  722. /*    Protocol descriptor
  723. */
  724.  
  725. PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0 };
  726.