home *** CD-ROM | disk | FTP | other *** search
/ Collection of Internet / Collection of Internet.iso / msdos / lynx / source / www / library / implemen / http.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-10-25  |  16.6 KB  |  609 lines

  1. /*    HyperText Tranfer Protocol    - Client implementation        HTTP.c
  2. **    ==========================
  3. **
  4. ** Bugs:
  5. **    Not implemented:
  6. **        Forward
  7. **        Redirection
  8. **        Error handling
  9. */
  10.  
  11. /*    Module parameters:
  12. **    -----------------
  13. **
  14. **  These may be undefined and redefined by syspec.h
  15. */
  16.  
  17. /*    MOSAIC_HACK2 is a kludge to guess the file type of trabsferred
  18. **    file from the URL.  It is STRICTLY illegal to do this!
  19. */
  20.  
  21. /* Implements:
  22. */
  23. #include"capalloc.h"
  24. #include "HTTP.h"
  25. #include"capstdio.h"
  26.  
  27. #define HTTP_VERSION    "HTTP/1.0"
  28. #define HTTP2                /* Version is greater than 0.9 */
  29.  
  30. #define INIT_LINE_SIZE        1024    /* Start with line buffer this big */
  31. #define LINE_EXTEND_THRESH    256    /* Minimum read size */
  32. #define VERSION_LENGTH         20    /* for returned protocol version */
  33.  
  34. /* Uses:
  35. */
  36. #include "HTParse.h"
  37. #include "HTUtils.h"
  38. #include "tcp.h"
  39. #include "HTTCP.h"
  40. #include "HTFormat.h"
  41. #include <ctype.h>
  42. #include "HTAlert.h"
  43. #include "HTMIME.h"
  44. #include "HTML.h"        /* SCW */
  45. #include "HTInit.h"        /* SCW */
  46. #include "HTAABrow.h"        /* Access Authorization */
  47.  
  48. struct _HTStream {
  49.     HTStreamClass * isa;        /* all we need to know */
  50. };
  51.  
  52.  
  53. extern char * HTAppName;    /* Application name: please supply */
  54. extern char * HTAppVersion;    /* Application version: please supply */
  55.  
  56. /*        Load Document from HTTP Server            HTLoadHTTP()
  57. **        ==============================
  58. **
  59. **    Given a hypertext address, this routine loads a document.
  60. **
  61. **
  62. ** On entry,
  63. **    arg    is the hypertext reference of the article to be loaded.
  64. **    gate    is nill if no gateway, else the gateway address.
  65. **
  66. ** On exit,
  67. **    returns    >=0    If no error, a good socket number
  68. **        <0    Error.
  69. **
  70. **    The socket must be closed by the caller after the document has been
  71. **    read.
  72. **
  73. */
  74. PUBLIC int HTLoadHTTP ARGS4 (
  75.     CONST char *,         arg,
  76. /*    CONST char *,        gate, */
  77.     HTParentAnchor *,    anAnchor,
  78.     HTFormat,        format_out,
  79.     HTStream*,        sink)
  80. {
  81.     int s;                /* Socket number for returned data */
  82.     char *command;            /* The whole command */
  83.     char * eol = 0;            /* End of line if found */
  84.     char * start_of_data;        /* Start of body of reply */
  85.     int length;                /* Number of valid bytes in buffer */
  86.     int status;                /* tcp return */
  87.     char crlf[3];            /* A CR LF equivalent string */
  88.     HTStream *    target = NULL;        /* Unconverted data */
  89.     HTFormat format_in;            /* Format arriving in the message */
  90.     char *auth = NULL;            /* Authorization information */
  91.     
  92.     CONST char* gate = 0;        /* disable this feature */
  93.     SockA soc_address;            /* Binary network address */
  94.     SockA * sin = &soc_address;
  95.     BOOL had_header = NO;        /* Have we had at least one header? */
  96.     char * text_buffer = NULL;
  97.     char * binary_buffer = NULL;
  98.     BOOL extensions = YES;        /* Assume good HTTP server */
  99.     if (!arg) return -3;        /* Bad if no name sepcified    */
  100.     if (!*arg) return -2;        /* Bad if name had zero length    */
  101.  
  102. /*  Set up defaults:
  103. */
  104. #ifdef DECNET
  105.     sin->sdn_family = AF_DECnet;        /* Family = DECnet, host order */
  106.     sin->sdn_objnum = DNP_OBJ;          /* Default: http object number */
  107. #else  /* Internet */
  108.     sin->sin_family = AF_INET;        /* Family = internet, host order */
  109.     sin->sin_port = htons(TCP_PORT);    /* Default: http port    */
  110. #endif
  111.  
  112.     sprintf(crlf, "%c%c", CR, LF);    /* To be corect on Mac, VM, etc */
  113. #ifndef RELEASE
  114.     if (TRACE) {
  115.     if (gate) fprintf(stderr,
  116.         "HTTPAccess: Using gateway %s for %s\n", gate, arg);
  117.     else fprintf(stderr, "HTTPAccess: Direct access for %s\n", arg);
  118.     }
  119. #endif /* RELEASE */
  120.     
  121. /* Get node name and optional port number:
  122. */
  123.     {
  124.     char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
  125.     int status = HTParseInet(sin, p1);  /* TBL 920622 */
  126.         free(p1);
  127.     if (status) return status;   /* No such host for example */
  128.     }
  129.     
  130. retry:
  131.  
  132. /*
  133. ** Compose authorization information (this was moved here
  134. ** from after the making of the connection so that the connection
  135. ** wouldn't have to wait while prompting username and password
  136. ** from the user).                -- AL 13.10.93
  137. */
  138. #ifdef ACCESS_AUTH
  139. #define FREE(x)    if (x) {free(x); x=NULL;}
  140.     {
  141.     char *docname;
  142.     char *hostname;
  143.     char *colon;
  144.     int portnumber;
  145.  
  146.     docname = HTParse(arg, "", PARSE_PATH);
  147.     hostname = HTParse((gate ? gate : arg), "", PARSE_HOST);
  148.     if (hostname &&
  149.         NULL != (colon = strchr(hostname, ':'))) {
  150.         *(colon++) = '\0';    /* Chop off port number */
  151.         portnumber = atoi(colon);
  152.     }
  153.     else portnumber = 80;
  154.     
  155.     auth = HTAA_composeAuth(hostname, portnumber, docname);
  156.  
  157. #ifndef RELEASE
  158.     if (TRACE) {
  159.         if (auth)
  160.         fprintf(stderr, "HTTP: Sending authorization: %s\n", auth);
  161.         else
  162.         fprintf(stderr, "HTTP: Not sending authorization (yet)\n");
  163.     }
  164. #endif /* RELEASE */
  165.     FREE(hostname);
  166.     FREE(docname);
  167.     }
  168. #endif /* ACCESS_AUTH */
  169.    
  170. /*    Now, let's get a socket set up from the server for the data:
  171. */      
  172. #ifdef DECNET
  173.     s = socket(AF_DECnet, SOCK_STREAM, 0);
  174. #else
  175.     s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  176. #endif
  177.     status = connect(s, (struct sockaddr*)&soc_address, sizeof(soc_address));
  178.     if (status < 0) {
  179. #ifndef RELEASE
  180.         if (TRACE) fprintf(stderr,
  181.           "HTTP: Unable to connect to remote host for `%s' (errno = %d).\n", arg, errno);
  182. #endif /* RELEASE */
  183.         /* free(command);   BUG OUT TBL 921121 */
  184.         return HTInetStatus("connect");
  185.       }
  186.     
  187. #ifndef RELEASE
  188.     if (TRACE) fprintf(stderr, "HTTP connected, socket %d\n", s);
  189. #endif /* RELEASE */
  190. /*    Ask that node for the document,
  191. **    omitting the host name & anchor if not gatewayed.
  192. */        
  193.     if (gate) {
  194.         command = malloc(4 + strlen(arg)+ 2 + 31);
  195.         if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
  196.         strcpy(command, "GET ");
  197.     strcat(command, arg);
  198.     } else { /* not gatewayed */
  199.     char * p1 = HTParse(arg, "", PARSE_PATH|PARSE_PUNCTUATION);
  200.         command = malloc(4 + strlen(p1)+ 2 + 31);
  201.         if (command == NULL) outofmem(__FILE__, "HTLoadHTTP");
  202.     strcpy(command, "GET ");
  203.  
  204.     /*
  205.      *    If we are using a proxy gateway don't copy in the first
  206.      *    slash of say: /gopher://whatever so that just gopher://* is
  207.      *    sent.
  208.      */
  209.     {
  210.         extern BOOL B_proxy;
  211.  
  212.         if(B_proxy == TRUE)    {
  213.             strcat(command, p1 + 1);
  214.         }
  215.         else    {
  216.             strcat(command, p1);
  217.         }
  218.     }
  219.     free(p1);
  220.     }
  221. #ifdef HTTP2
  222.     if (extensions) {
  223.         strcat(command, " ");
  224.         strcat(command, HTTP_VERSION);
  225.     }
  226. #endif
  227.  
  228.     strcat(command, crlf);    /* CR LF, as in rfc 977 */
  229.  
  230.     if (extensions) {
  231.  
  232.     int n;
  233.     int i;
  234.         HTAtom * present = WWW_PRESENT;
  235.     char line[256];    /*@@@@ */
  236.  
  237.     if (!HTPresentations) HTFormatInit();
  238.     n = HTList_count(HTPresentations);
  239.  
  240.     for(i=0; i<n; i++) {
  241.         HTPresentation * pres = HTList_objectAt(HTPresentations, i);
  242.         if (pres->rep_out == present) {
  243.           if (pres->quality != 1.0) {
  244.                  sprintf(line, "Accept: %s q=%.3f%c%c",
  245.              HTAtom_name(pres->rep), pres->quality, CR, LF);
  246.           } else {
  247.                  sprintf(line, "Accept: %s%c%c",
  248.              HTAtom_name(pres->rep), CR, LF);
  249.           }
  250.           StrAllocCat(command, line);
  251.  
  252.         }
  253.     }
  254.     
  255.     sprintf(line, "User-Agent:  %s/%s  libwww/%s%c%c",
  256.         HTAppName ? HTAppName : "unknown",
  257.         HTAppVersion ? HTAppVersion : "0.0",
  258.         HTLibraryVersion, CR, LF);
  259.           StrAllocCat(command, line);
  260.  
  261. #ifdef ACCESS_AUTH
  262.     if (auth != NULL) {
  263.         sprintf(line, "%s%c%c", auth, CR, LF);
  264.         StrAllocCat(command, line);
  265.     }
  266. #endif /* ACCESS_AUTH */
  267.     }
  268.  
  269.     StrAllocCat(command, crlf);    /* Blank line means "end" */
  270.  
  271. #ifndef RELEASE
  272.     if (TRACE) fprintf(stderr, "HTTP Tx: %s\n", command);
  273. #endif /* RELEASE */
  274. /*    Translate into ASCII if necessary
  275. */
  276. #ifdef NOT_ASCII
  277.     {
  278.         char * p;
  279.     for(p = command; *p; p++) {
  280.         *p = TOASCII(*p);
  281.     }
  282.     }
  283. #endif
  284.  
  285.     status = NETWRITE(s, command, (int)strlen(command));
  286.     free(command);
  287.     if (status<0) {
  288. #ifndef RELEASE
  289.     if (TRACE) fprintf(stderr, "HTTPAccess: Unable to send command.\n");
  290. #endif /* RELEASE */
  291.         return HTInetStatus("send");
  292.     }
  293.  
  294.  
  295. /*    Read the first line of the response
  296. **    -----------------------------------
  297. **
  298. **    HTTP0 servers must return ASCII style text, though it can in
  299. **    principle be just text without any markup at all.
  300. **    Full HTTP servers must return a response
  301. **    line and RFC822 style header.  The response must therefore in
  302. **    either case have a CRLF somewhere soon.
  303. **
  304. **    This is the theory.  In practice, there are (1993) unfortunately
  305. **    many binary documents just served up with HTTP0.9.  This
  306. **    means we have to preserve the binary buffer (on the assumption that
  307. **    conversion from ASCII may lose information) in case it turns
  308. **    out that we want the binary original.
  309. */
  310.  
  311.     {
  312.     
  313.     /* Get numeric status etc */
  314.  
  315.     BOOL end_of_file = NO;
  316.     HTAtom * encoding = HTAtom_for("7bit");
  317.     int buffer_length = INIT_LINE_SIZE;    /* Why not? */
  318.     
  319.     binary_buffer = (char *) malloc(buffer_length * sizeof(char));
  320.     if (!binary_buffer) outofmem(__FILE__, "HTLoadHTTP");
  321.     text_buffer = (char *) malloc(buffer_length * sizeof(char));
  322.     if (!text_buffer) outofmem(__FILE__, "HTLoadHTTP");
  323.     length = 0;
  324.     
  325.     do {    /* Loop to read in the first line */
  326.         
  327.        /* Extend line buffer if necessary for those crazy WAIS URLs ;-) */
  328.        
  329.         if (buffer_length - length < LINE_EXTEND_THRESH) {
  330.             buffer_length = buffer_length + buffer_length;
  331.         binary_buffer = (char *) realloc(
  332.             binary_buffer, buffer_length * sizeof(char));
  333.         if (!binary_buffer) outofmem(__FILE__, "HTLoadHTTP");
  334.         text_buffer = (char *) realloc(
  335.             text_buffer, buffer_length * sizeof(char));
  336.         if (!text_buffer) outofmem(__FILE__, "HTLoadHTTP");
  337.         }
  338.         status = NETREAD(s, binary_buffer + length,
  339.                     buffer_length - length -1);
  340.         if (status < 0) {
  341.             HTAlert("Unexpected network read error on response");
  342.         NETCLOSE(s);
  343.         return status;
  344.         }
  345.  
  346. #ifndef RELEASE
  347.         if (TRACE) fprintf(stderr, "HTTP: read returned %d bytes.\n",
  348.          status);
  349. #endif /* RELEASE */
  350.  
  351.         if (status == 0) {
  352.             end_of_file = YES;
  353.         break;
  354.         }
  355.         binary_buffer[length+status] = 0;
  356.  
  357.  
  358. /*    Make an ASCII *copy* of the buffer
  359. */
  360. #ifdef NOT_ASCII
  361. #ifndef RELEASE
  362.         if (TRACE) fprintf(stderr, "Local codes CR=%d, LF=%d\n", CR, LF);
  363. #endif /* RELEASE */
  364. #endif
  365.         {
  366.         char * p;
  367.         char * q;
  368.         for(p = binary_buffer+length, q=text_buffer+length;
  369.             *p; p++, q++) {
  370.             *q = FROMASCII(*p);
  371.         }
  372.  
  373.         *q++ = 0;
  374.         }
  375.  
  376. /* Kludge to trap binary responses from illegal HTTP0.9 servers.
  377. ** First time we have enough, look at the stub in ASCII
  378. ** and get out of here if it doesn't look right.
  379. **
  380. ** We also check for characters above 128 in the first few bytes, and
  381. ** if we find them we forget the html default.
  382. **
  383. ** Bugs: A HTTP0.9 server returning a document starting "HTTP/"
  384. **    will be taken as a HTTP 1.0 server.  Failure.
  385. **    An HTTP 0.9 server returning a binary document with
  386. **    characters < 128 will be read as ASCII.
  387. */
  388. #define STUB_LENGTH 20
  389.         if (length < STUB_LENGTH && length+status >= STUB_LENGTH) {
  390.         if(strncmp("HTTP/", text_buffer, 5)!=0) {
  391.             char *p;
  392.             start_of_data = text_buffer; /* reparse whole reply */
  393.             for(p=binary_buffer; p <binary_buffer+STUB_LENGTH;p++) {
  394.                 if (((int)*p)&128) {
  395.                 format_in = HTAtom_for("www/unknown");
  396.                 length = length + status;
  397.                 goto copy; /* out of while loop */
  398.             }
  399.             }
  400.         }
  401.         }
  402. /* end kludge */
  403.  
  404.         
  405.         eol = strchr(text_buffer + length, 10);        
  406.         if (eol) {
  407.             *eol = 0;        /* Terminate the line */
  408.             if (eol[-1] == CR) eol[-1] = 0;    /* Chop trailing CR */
  409.                                         /* = corrected to ==  -- AL  */
  410.             }
  411.  
  412.         length = length + status;
  413.  
  414.     } while (!eol && !end_of_file);        /* No LF */        
  415.             
  416.     } /* Scope of loop variables */
  417.  
  418.     
  419. /*    We now have a terminated unfolded line. Parse it.
  420. **    -------------------------------------------------
  421. */
  422.  
  423. #ifndef RELEASE
  424.     if (TRACE)fprintf(stderr, "HTTP: Rx: %.70s\n", text_buffer);
  425. #endif /* RELEASE */
  426.  
  427.     {
  428.     int fields;
  429.     char server_version [VERSION_LENGTH+1];
  430.     int server_status;
  431.  
  432.  
  433. /* Kludge to work with old buggy servers.  They can't handle the third word
  434. ** so we try again without it.
  435. */
  436.     if (extensions &&
  437.         0==strcmp(text_buffer,        /* Old buggy server? */
  438.         "Document address invalid or access not authorised")) {
  439.         extensions = NO;
  440.         if (binary_buffer) free(binary_buffer);
  441.         if (text_buffer) free(text_buffer);
  442. #ifndef RELEASE
  443.         if (TRACE) fprintf(stderr,
  444.         "HTTP: close socket %d to retry with HTTP0\n", s);
  445. #endif /* RELEASE */
  446.         NETCLOSE(s);
  447.         goto retry;        /* @@@@@@@@@@ */
  448.     }
  449. /* end kludge */
  450.  
  451.     fields = sscanf(text_buffer, "%20s%d",
  452.         server_version,
  453.         &server_status);
  454.  
  455.     if (fields < 2 || 
  456.            strncmp(server_version, "HTTP/", 5)!=0) { /* HTTP0 reply */
  457.         format_in = WWW_HTML;
  458.         start_of_data = text_buffer;    /* reread whole reply */
  459.         if (eol) *eol = '\n';        /* Reconstitute buffer */
  460.         
  461.     } else {                /* Full HTTP reply */
  462.     
  463.     /*    Decode full HTTP response */
  464.     
  465.         format_in = HTAtom_for("www/mime");
  466.         start_of_data = eol ? eol + 1 : text_buffer + length;
  467.  
  468.         switch (server_status / 100) {
  469.         
  470.         default:        /* bad number */
  471.         HTAlert("Unknown status reply from server!");
  472.         break;
  473.         
  474.         case 3:        /* Various forms of redirection */
  475.         HTAlert(
  476.     "Redirection response from server is not handled by this client");
  477.         break;
  478.         
  479.         case 4:        /* Access Authorization problem */
  480. #ifdef ACCESS_AUTH
  481.         switch (server_status) {
  482.           case 401:
  483.             length -= start_of_data - text_buffer;
  484.             if (HTAA_shouldRetryWithAuth(start_of_data, length, s)) {
  485.             /* Clean up before retrying */
  486.             if (binary_buffer) free(binary_buffer);
  487.             if (text_buffer) free(text_buffer);
  488. #ifndef RELEASE
  489.             if (TRACE)
  490.                 fprintf(stderr, "%s %d %s\n",
  491.                     "HTTP: close socket", s,
  492.                     "to retry with Access Authorization");
  493. #endif /* RELEASE */
  494.             (void)NETCLOSE(s);
  495.             goto retry;
  496.             break;
  497.             }
  498.             else {
  499.             /* FALL THROUGH */
  500.             }
  501.           default:
  502.             {
  503.             char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
  504.             char * message;
  505.  
  506.             if (!(message = (char*)malloc(strlen(text_buffer) +
  507.                               strlen(p1) + 100)))
  508.                 outofmem(__FILE__, "HTTP 4xx status");
  509.             sprintf(message,
  510.                 "HTTP server at %s replies:\n%s\n\n%s\n",
  511.                 p1, text_buffer,
  512.                 ((server_status == 401) 
  513.                  ? "Access Authorization package giving up.\n"
  514.                  : ""));
  515.             status = HTLoadError(sink, server_status, message);
  516.             free(message);
  517.             free(p1);
  518.             goto clean_up;
  519.             }
  520.         } /* switch */
  521.         goto clean_up;
  522.         break;
  523. #else
  524.         /* case 4 without Access Authorization falls through */
  525.         /* to case 5 (previously "I think I goofed").  -- AL */
  526. #endif /* ACCESS_AUTH */
  527.  
  528.         case 5:        /* I think you goofed */
  529.         {
  530.             char *p1 = HTParse(gate ? gate : arg, "", PARSE_HOST);
  531.             char * message = (char*)malloc(
  532.             strlen(text_buffer)+strlen(p1) + 100);
  533.             if (!message) outofmem(__FILE__, "HTTP 5xx status");
  534.             sprintf(message,
  535.             "HTTP server at %s replies:\n%s", p1, text_buffer);
  536.             status = HTLoadError(sink, server_status, message);
  537.             free(message);
  538.             free(p1);
  539.             goto clean_up;
  540.         }
  541.         break;
  542.         
  543.         case 2:        /* Good: Got MIME object */
  544.         break;
  545.  
  546.         } /* switch on response code */
  547.     
  548.     }        /* Full HTTP reply */
  549.     
  550.     } /* scope of fields */
  551.  
  552. /*    Set up the stream stack to handle the body of the message
  553. */
  554.  
  555. copy:
  556.  
  557.     target = HTStreamStack(format_in,
  558.             format_out,
  559.             sink , anAnchor);
  560.  
  561.     if (!target) {
  562.     char buffer[1024];    /* @@@@@@@@ */
  563.     if (binary_buffer) free(binary_buffer);
  564.     if (text_buffer) free(text_buffer);
  565.     sprintf(buffer, "Sorry, no known way of converting %s to %s.",
  566.         HTAtom_name(format_in), HTAtom_name(format_out));
  567.     fprintf(stderr, "HTTP: %s", buffer);
  568.     status = HTLoadError(sink, 501, buffer);
  569.     goto clean_up;
  570.     }
  571.  
  572.     
  573. /*    Push the data down the stream
  574. **    We have to remember the end of the first buffer we just read
  575. */
  576.     if (format_in == WWW_HTML) {
  577.         target = HTNetToText(target);    /* Pipe through CR stripper */
  578.     }
  579.     
  580.     (*target->isa->put_block)(target,
  581.         binary_buffer + (start_of_data - text_buffer),
  582.         length - (start_of_data - text_buffer));
  583.     HTCopy(s, target);
  584.     
  585.     (*target->isa->free)(target);
  586.     status = HT_LOADED;
  587.  
  588. /*    Clean up
  589. */
  590.     
  591. clean_up: 
  592.     if (binary_buffer) free(binary_buffer);
  593.     if (text_buffer) free(text_buffer);
  594.  
  595. #ifndef RELEASE
  596.     if (TRACE) fprintf(stderr, "HTTP: close socket %d.\n", s);
  597. #endif /* RELEASE */
  598.     (void) NETCLOSE(s);
  599.  
  600.     return status;            /* Good return */
  601.  
  602. }
  603.  
  604.  
  605. /*    Protocol descriptor
  606. */
  607.  
  608. GLOBALDEF PUBLIC HTProtocol HTTP = { "http", HTLoadHTTP, 0 };
  609.