home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / lynx-2.4 / WWW / Library / Implementation / HTWAIS.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-06-28  |  27.2 KB  |  1,058 lines

  1. /*    WorldWideWeb - Wide Area Informaion Server Access    HTWAIS.c
  2. **    ==================================================
  3. **
  4. **    This module allows a WWW server or client to read data from a
  5. **    remote  WAIS
  6. **  server, and provide that data to a WWW client in hypertext form.
  7. **  Source files, once retrieved, are stored and used to provide
  8. **  information about the index when that is acessed.
  9. **
  10. ** Authors
  11. **    BK    Brewster Kahle, Thinking Machines, <Brewster@think.com>
  12. **    TBL    Tim Berners-Lee, CERN <timbl@info.cern.ch>
  13. **    FM    Foteos Macrides, WFEB <macrides@sci.wfeb.edu>
  14. **
  15. ** History
  16. **       Sep 91    TBL adapted shell-ui.c (BK) with HTRetrieve.c from WWW.
  17. **       Feb 91    TBL Generated HTML cleaned up a bit (quotes, escaping)
  18. **                Refers to lists of sources. 
  19. **       Mar 93    TBL Lib 2.0 compatible module made.
  20. **       May 94    FM  Added DIRECT_WAIS support for VMS.
  21. **
  22. ** Bugs
  23. **    Uses C stream i/o to read and write sockets, which won't work
  24. **    on VMS TCP systems.
  25. **
  26. **    Should cache connections.
  27. **
  28. **    ANSI C only as written
  29. **
  30. ** Bugs fixed
  31. **      NT Nathan Torkington (Nathan.Torkington@vuw.ac.nz)
  32. **
  33. ** WAIS comments:
  34. **
  35. **    1.    Separate directories for different system's .o would help
  36. **    2.    Document ids are rather long!
  37. **
  38. ** WWW Address mapping convention:
  39. **
  40. **    /servername/database/type/length/document-id
  41. **
  42. **    /servername/database?word+word+word
  43. */
  44. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  45.    No guarantees or restrictions.  See the readme file for the full standard
  46.    disclaimer.
  47.  
  48.    Brewster@think.com
  49. */
  50.  
  51. #include "HTUtils.h"
  52. #include "tcp.h"
  53. #include "HTParse.h"
  54. #include "HTAccess.h"        /* We implement a protocol */
  55. #include "HTML.h"        /* The object we will generate */
  56. #include "HTFormat.h"
  57. #include "HTTCP.h"
  58. /* #include "HTWSRC.h"    */    /* Need some bits from here */
  59. /* #include "ParseWSRC.h" */
  60.  
  61.  
  62. /*            From WAIS
  63. **            ---------
  64. */
  65.  
  66. #ifdef VMS
  67. #include "HTVMS_WaisUI.h"
  68. #include "HTVMS_WaisProt.h"
  69. #else
  70. #include <ui.h>
  71. #endif /* VMS */
  72.  
  73. #define MAX_MESSAGE_LEN 100000
  74. #define CHARS_PER_PAGE 10000 /* number of chars retrieved in each request */
  75.  
  76. #define WAISSEARCH_DATE "Fri Jul 19 1991"
  77.  
  78.  
  79. /*            FROM WWW
  80. **            --------
  81. */
  82. #include "LYLeaks.h"
  83.  
  84. #define DIRECTORY "/cnidr.org:210/directory-of-servers"
  85. /* #define DIRECTORY "/quake.think.com:210/directory-of-servers" */
  86.  
  87. #define BIG 1024    /* identifier size limit  @@@@@ */
  88.  
  89. #define BUFFER_SIZE 4096    /* Arbitrary size for efficiency */
  90.  
  91. #define HEX_ESCAPE '%'
  92.  
  93. extern int WWW_TraceFlag;    /* Control diagnostic output */
  94. extern FILE * logfile;        /* Log file output */
  95.  
  96. PRIVATE BOOL    as_gate;    /* Client is using us as gateway */
  97.  
  98. PRIVATE char    line[2048];    /* For building strings to display */
  99.                 /* Must be able to take id */
  100.  
  101. #define PUTC(c) (*target->isa->put_character)(target, c)
  102. #define PUTS(s) (*target->isa->put_string)(target, s)
  103. #define START(e) (*target->isa->start_element)(target, e, 0, 0)
  104. #define END(e) (*target->isa->end_element)(target, e)
  105. #define FREE_TARGET (*target->isa->_free)(target)
  106.  
  107. struct _HTStructured {
  108.     CONST HTStructuredClass *    isa;
  109.     /* ... */
  110. };
  111.  
  112. struct _HTStream {
  113.     CONST HTStreamClass *    isa;
  114.     /* ... */
  115. };
  116.  
  117.  
  118. /* ------------------------------------------------------------------------ */
  119. /* ---------------- Local copy of connect_to_server calls ----------------- */
  120. /* ------------------------------------------------------------------------ */
  121.  
  122. /* Returns 1 on success, 0 on fail, -1 on interrupt. */
  123. static int fd_mosaic_connect_to_server ARGS3(char *, host_name, long, port,
  124. long *, fd)
  125. {
  126.   /* New version. */
  127.   char dummy[256];
  128.   int status;
  129.  
  130.   sprintf (dummy, "wais://%s:%d/", host_name, port);
  131.  
  132.   status = HTDoConnect (dummy, "WAIS", 210, (int *)fd);
  133.   if (status == HT_INTERRUPTED)
  134.     {
  135.       if (TRACE)
  136.         fprintf (stderr, "===WAIS=== interrupted in connect\n");
  137.       HTAlert ("Connection interrupted.");
  138.       return -1;
  139.     }
  140.   if (status < 0)
  141.     return 0;
  142.   return 1;
  143. }
  144.  
  145. /* Returns 1 on success, 0 on fail, -1 on interrupt. */
  146. static int mosaic_connect_to_server ARGS3(char *, host_name, long, port,
  147. FILE **, fp)
  148. {
  149.   FILE* file;
  150.   long fd;
  151.   int rv;
  152.   
  153.   rv = fd_mosaic_connect_to_server (host_name, port, &fd);
  154.   if(rv == 0) 
  155.     {
  156.       HTAlert ("Could not connect to WAIS server.");
  157.       return 0;
  158.     }
  159.   else if (rv == -1)
  160.     {
  161.       HTAlert ("Connection interrupted.");
  162.       return -1;
  163.     }
  164.  
  165. #ifndef VMS
  166.   if ((file = fdopen(fd,"r+")) == NULL) 
  167.     {
  168.       HTAlert ("Could not open WAIS connection for reading.");
  169.       return 0;
  170.     }
  171.  
  172.   *fp = file;
  173. #else
  174.   *fp = (FILE *)fd;
  175. #endif /* VMS */
  176.   return 1;
  177. }
  178.  
  179.  
  180. /* ------------------------------------------------------------------------ */
  181. /* ------------------------------------------------------------------------ */
  182.  
  183.  
  184. /*                                showDiags
  185. */
  186. /* modified from Jonny G's version in ui/question.c */
  187.  
  188. void showDiags ARGS2(
  189.     HTStream *,         target,
  190.     diagnosticRecord **,     d)
  191. {
  192.   long i;
  193.  
  194.   for (i = 0; d[i] != NULL; i++) {
  195.     if (d[i]->ADDINFO != NULL) {
  196.       PUTS("Diagnostic code is ");
  197.       PUTS(d[i]->DIAG);
  198.       PUTC(' ');
  199.       PUTS(d[i]->ADDINFO);
  200.       PUTC('\n'); ;
  201.     }
  202.   }
  203. }
  204.  
  205. /*    Matrix of allowed characters in filenames
  206. **    -----------------------------------------
  207. */
  208.  
  209. PRIVATE BOOL acceptable[256];
  210. PRIVATE BOOL acceptable_inited = NO;
  211.  
  212. PRIVATE void init_acceptable NOARGS
  213. {
  214.     unsigned int i;
  215.     char * good = 
  216.       "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./-_$";
  217.     for(i=0; i<256; i++) acceptable[i] = NO;
  218.     for(;*good; good++) acceptable[(unsigned int)*good] = YES;
  219.     acceptable_inited = YES;
  220. }
  221.  
  222. /*    Transform file identifier into WWW address
  223. **    ------------------------------------------
  224. **
  225. **
  226. ** On exit,
  227. **    returns        nil if error
  228. **            pointer to malloced string (must be freed) if ok
  229. */
  230. char * WWW_from_archie ARGS1 (char *, file)
  231. {
  232.     char * end;
  233.     char * result;
  234.     char * colon;
  235.     for(end=file; *end > ' '; end++);    /* assumes ASCII encoding*/
  236.     result = (char *)malloc(10 + (end-file));
  237.     if (!result) return result;        /* Malloc error */
  238.     strcpy(result, "file://");
  239.     strncat(result, file, end-file);
  240.     colon = strchr(result+7, ':');    /* Expect colon after host */
  241.     if (colon) {
  242.     for(; colon[0]; colon[0]=colon[1], colon++);    /* move down */
  243.     }
  244.     return result;
  245. } /* WWW_from_archie */
  246.  
  247. /*    Transform document identifier into URL
  248. **    --------------------------------------
  249. **
  250. ** Bugs: A static buffer of finite size is used!
  251. **    The format of the docid MUST be good!
  252. **
  253. ** On exit,
  254. **    returns        nil if error
  255. **            pointer to malloced string (must be freed) if ok
  256. */
  257. PRIVATE char hex [17] = "0123456789ABCDEF";
  258. extern char from_hex PARAMS((char a));            /* In HTWSRC @@ */
  259.  
  260. PRIVATE char * WWW_from_WAIS ARGS1(any *, docid)
  261.  
  262. {
  263.     static char buf[BIG];
  264.     char * q = buf;
  265.     char * p = (docid->bytes);
  266.     int i, l;
  267.     if (TRACE) {
  268.     char *p;
  269.     fprintf(stderr, "WAIS id (%d bytes) is ", (int)docid->size);
  270.     for(p=docid->bytes; p<docid->bytes+docid->size; p++) {
  271.         if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */
  272.         fprintf(stderr, "%c", *p);
  273.         else
  274.         fprintf(stderr, "<%x>", (unsigned)*p);
  275.     }
  276.     fprintf(stderr, "\n");
  277.     }     
  278.     for (p=docid->bytes; (p<docid->bytes+docid->size) && (q<&buf[BIG]);) {
  279.     if (TRACE) fprintf(stderr, "    Record type %d, length %d\n",
  280.         p[0], p[1]);
  281.         if (*p>10) {
  282.         if(TRACE)
  283.             fprintf(stderr, "Eh? DOCID record type of %d!\n", *p);
  284.         return 0;
  285.     }
  286.     {    /* Bug fix -- allow any byte value 15 Apr 93 */
  287.         unsigned int i = (unsigned) *p++;
  288.         
  289.         if (i > 99) {
  290.         *q++ = (i/100) + '0';
  291.         i = i % 100;
  292.         }
  293.         if (i > 9) {
  294.         *q++ = (i/10) + '0';
  295.         i = i % 10;
  296.         }
  297.         *q++ = i + '0';    /* Record type */
  298.     }
  299.     *q++ = '=';        /* Separate */
  300.     l = *p++;        /* Length */
  301.     for(i=0; i<l; i++, p++){
  302.         if (!acceptable[*p]) {
  303.         *q++ = HEX_ESCAPE;    /* Means hex commming */
  304.         *q++ = hex[(*p) >> 4];
  305.         *q++ = hex[(*p) & 15];
  306.         }
  307.         else *q++ = *p;
  308.     }
  309.     *q++= ';';        /* Terminate field */
  310.     }
  311.     *q++ = 0;            /* Terminate string */
  312.     if (TRACE) fprintf(stderr, "WWW form of id: %s\n", buf); 
  313.     {
  314.         char * result = (char *)malloc(strlen(buf)+1);
  315.     if (!result)
  316.         outofmem(__FILE__, "WWW_from_WAIS");
  317.     strcpy(result, buf);
  318.     return result;
  319.     }
  320. } /* WWW_from_WAIS */
  321.  
  322.  
  323. /*    Transform URL into WAIS document identifier
  324. **    -------------------------------------------
  325. **
  326. ** On entry,
  327. **    docname        points to valid name produced originally by
  328. **            WWW_from_WAIS
  329. ** On exit,
  330. **    docid->size    is valid
  331. **    docid->bytes    is malloced and must later be freed.
  332. */
  333. PRIVATE any * WAIS_from_WWW ARGS2 (any *, docid, char *, docname)
  334. {
  335.     char *z;     /* Output pointer */
  336.     char *sor;    /* Start of record - points to size field. */
  337.     char *p;     /* Input pointer */
  338.     char *q;     /* Poisition of "=" */
  339.     char *s;     /* Position of semicolon */
  340.     int n;    /* size */
  341.     if (TRACE) fprintf(stderr, "WWW id (to become WAIS id): %s\n", docname); 
  342.     for(n=0, p = docname; *p; p++) {    /* Count sizes of strings */
  343.         n++;
  344.     if (*p == ';')  n--;        /* Not converted */
  345.     else if (*p == HEX_ESCAPE) n=n-2;    /* Save two bytes */
  346.         docid->size = n;
  347.     }
  348.     
  349.     if (!(docid->bytes = (char *) malloc(docid->size))) /* result record */
  350.         outofmem(__FILE__, "WAIS_from_WWW");
  351.     z = docid->bytes;
  352.     
  353.     for(p = docname; *p; ) {    /* Convert of strings */
  354.                     /* Record type */
  355.                 
  356.     *z = 0;            /* Initialize record type */
  357.     while (*p >= '0' && *p <= '9') {
  358.         *z = *z*10 + (*p++ - '0');    /* Decode decimal record type */
  359.     }
  360.     z++;
  361.     if (*p != '=') return 0;
  362.     q = p;
  363.     
  364. /*        *z++ = *p++ - '0';
  365.     q = strchr(p , '=');
  366.     if (!q) return 0;
  367. */
  368.     s = strchr(q, ';');    /* (Check only) */
  369.     if (!s) return 0;    /* Bad! No ';';    */
  370.         sor = z;        /* Remember where the size field was */
  371.     z++;            /* Skip record size for now    */
  372.     for(p=q+1; *p!=';' ; ) {
  373.        if (*p == HEX_ESCAPE) {
  374.             char c;
  375.         unsigned int b;
  376.         p++;
  377.             c = *p++;
  378.         b =   from_hex(c);
  379.         c = *p++;
  380.         if (!c) break;    /* Odd number of chars! */
  381.         *z++ = (b<<4) + from_hex(c);
  382.         } else {
  383.             *z++ = *p++;    /* Record */
  384.         }
  385.     }
  386.     *sor = (z-sor-1);    /* Fill in size -- not counting size itself */
  387.     p++;            /* After semicolon: start of next record */
  388.     }
  389.     
  390.     if (TRACE) {
  391.     char *p;
  392.     fprintf(stderr, "WAIS id (%d bytes) is ", (int)docid->size);
  393.     for(p=docid->bytes; p<docid->bytes+docid->size; p++) {
  394.         if ((*p >= ' ') && (*p<= '~')) /* Assume ASCII! */
  395.         fprintf(stderr, "%c", *p);
  396.         else
  397.         fprintf(stderr, "<%x>", (unsigned)*p);
  398.     }
  399.     fprintf(stderr, "\n");
  400.     }     
  401.     return docid;        /* Ok */
  402.     
  403. } /* WAIS_from_WWW */
  404.  
  405.  
  406. /*    Send a plain text record to the client        output_text_record()
  407. **    --------------------------------------
  408. */
  409.  
  410. PRIVATE void output_text_record ARGS4(
  411.     HTStream *,            target,
  412.     WAISDocumentText *,        record,
  413.     boolean,            quote_string_quotes,
  414.     boolean,                    binary)
  415. {
  416.   long count;
  417.   /* printf(" Text\n");
  418.      print_any("     DocumentID:  ", record->DocumentID);
  419.      printf("     VersionNumber:  %d\n", record->VersionNumber);
  420.      */
  421.  
  422.   if (binary) {
  423.     (*target->isa->put_block)(target,
  424.                   record->DocumentText->bytes,
  425.                   record->DocumentText->size);
  426.     return;
  427.   }
  428.  
  429.   for(count = 0; count < record->DocumentText->size; count++){
  430.     long ch = (unsigned char)record->DocumentText->bytes[count];
  431.     if (ch == 27) {    /* What is this in for? Tim */
  432.  
  433.         /* then we have an escape code */
  434.         /* if the next letter is '(' or ')', then ignore two letters */
  435.         if('(' == record->DocumentText->bytes[count + 1] ||
  436.         ')' == record->DocumentText->bytes[count + 1])
  437.         count += 1;             /* it is a term marker */
  438.         else count += 4;        /* it is a paragraph marker */
  439.     } else if (ch == '\n' || ch == '\r') {
  440.         PUTC('\n');
  441.     } else if ((ch=='\t') || isprint(ch)){
  442.         PUTC(ch);
  443.     } 
  444.   }
  445. } /* output text record */
  446.  
  447.  
  448.  
  449. /*    Format A Search response for the client        display_search_response
  450. **    ---------------------------------------
  451. */
  452. /* modified from tracy shen's version in wutil.c
  453.  * displays either a text record or a set of headlines.
  454.  */
  455. void
  456. display_search_response ARGS4(
  457.     HTStructured *,        target,
  458.     SearchResponseAPDU *,    response,
  459.     char *,            database,
  460.     char *,             keywords)
  461. {
  462.   WAISSearchResponse  *info;
  463.   long i, k;
  464.   
  465.   BOOL archie =  strstr(database, "archie")!=0;    /* Specical handling */
  466.   
  467.   if (TRACE) fprintf(stderr, "HTWAIS: Displaying search response\n");
  468.   sprintf(line,
  469.       "Index %s contains the following %d item%s relevant to '%s'.\n",
  470.      database,
  471.      (int)(response->NumberOfRecordsReturned),
  472.      response->NumberOfRecordsReturned ==1 ? "" : "s",
  473.      keywords);
  474.  
  475.   PUTS(line);
  476.   PUTS("The first figure after each entry is its relative score, ");
  477.   PUTS("the second is the number of lines in the item.");
  478.   START(HTML_P);
  479.   PUTS("\n");
  480.   START(HTML_OL);
  481.  
  482.   if ( response->DatabaseDiagnosticRecords != 0 ) {
  483.     info = (WAISSearchResponse *)response->DatabaseDiagnosticRecords;
  484.     i =0; 
  485.  
  486.     if (info->Diagnostics != NULL)
  487.       showDiags((HTStream*)target, info->Diagnostics);
  488.  
  489.     if ( info->DocHeaders != 0 ) {
  490.       for (k=0; info->DocHeaders[k] != 0; k++ ) {
  491.     WAISDocumentHeader* head = info->DocHeaders[k];
  492.     char * headline = trim_junk(head->Headline);
  493.     any * docid = head->DocumentID;
  494.     char * docname;            /* printable version of docid */
  495.     i++;
  496.  
  497. /*    Make a printable string out of the document id.
  498. */
  499.     if (TRACE) fprintf(stderr, 
  500.         "HTWAIS:  %2ld: Score: %4ld, lines:%4ld '%s'\n", 
  501.            i,
  502.            (long int)(info->DocHeaders[k]->Score),
  503.            (long int)(info->DocHeaders[k]->Lines),
  504.            headline);
  505.  
  506.     START(HTML_LI);
  507.  
  508.     if (archie) {
  509.         char * www_name = WWW_from_archie(headline);
  510.         if (www_name) {
  511.         HTStartAnchor(target, NULL, www_name);
  512.         PUTS(headline);
  513.         
  514.         END(HTML_A);
  515.         free(www_name);
  516.         } else {
  517.          PUTS(headline);
  518.          PUTS(" (bad file name)");
  519.         }
  520.     } else { /* Not archie */
  521.         docname =  WWW_from_WAIS(docid);
  522.         if (docname) {
  523.         char * dbname = HTEscape(database, URL_XPALPHAS);
  524.         sprintf(line, "%s/%s/%d/%s",        /* W3 address */
  525.                     dbname,
  526.             head->Types ? head->Types[0] : "TEXT",
  527.             (int)(head->DocumentLength),
  528.             docname);
  529.         HTStartAnchor(target, NULL, ( (head->Types) 
  530.               && (!strcmp(head->Types[0], "URL"))) ? 
  531.                   headline : line); /* NT, Sep 93 */
  532.         PUTS(headline);
  533.         END(HTML_A);
  534.         free(dbname);
  535.         free(docname);
  536.         } else {
  537.          PUTS("(bad doc id)");
  538.         }
  539.       }
  540.  
  541.     sprintf(line, "%5ld  %5ld  ",
  542.         head->Score,
  543.         head->Lines);
  544.     PUTS( line);
  545.       } /* next document header */
  546.     } /* if there were any document headers */
  547.     
  548.     if ( info->ShortHeaders != 0 ) {
  549.       k =0;
  550.       while (info->ShortHeaders[k] != 0 ) {
  551.     i++;
  552.     PUTS( "(Short Header record, can't display)");
  553.       }
  554.     }
  555.     if ( info->LongHeaders != 0 ) {
  556.       k =0;
  557.       while (info->LongHeaders[k] != 0) {
  558.     i++;
  559.     PUTS( "\nLong Header record, can't display\n");
  560.       }
  561.     }
  562.     if ( info->Text != 0 ) {
  563.       k =0;
  564.       while (info->Text[k] != 0) {
  565.     i++;
  566.     PUTS( "\nText record\n");
  567.     output_text_record((HTStream*)target, info->Text[k++], false, false);
  568.       }
  569.     }
  570.     if ( info->Headlines != 0 ) {
  571.       k =0;
  572.       while (info->Headlines[k] != 0) {
  573.     i++;
  574.     PUTS( "\nHeadline record, can't display\n");
  575.     /* dsply_headline_record( info->Headlines[k++]); */
  576.       }
  577.     }
  578.     if ( info->Codes != 0 ) {
  579.       k =0;
  580.       while (info->Codes[k] != 0) {
  581.     i++;
  582.     PUTS( "\nCode record, can't display\n");
  583.     /* dsply_code_record( info->Codes[k++]); */
  584.       }
  585.     }
  586.   }                /* Loop: display user info */
  587.   END(HTML_OL);
  588.   PUTC('\n'); ;
  589. }
  590.  
  591.  
  592.  
  593.  
  594. /*        Load by name                    HTLoadWAIS
  595. **        ============
  596. **
  597. **    This renders any object or search as required
  598. */
  599. PUBLIC int HTLoadWAIS ARGS4(
  600.     CONST char *,        arg,
  601.     HTParentAnchor *,    anAnchor,
  602.     HTFormat,        format_out,
  603.     HTStream*,        sink)
  604.  
  605. #define MAX_KEYWORDS_LENGTH 1000
  606. #define MAX_SERVER_LENGTH 1000
  607. #define MAX_DATABASE_LENGTH 1000
  608. #define MAX_SERVICE_LENGTH 1000
  609. #define MAXDOCS 200
  610.  
  611. {
  612.     static CONST char * error_header =
  613. "<h1>Access error</h1>\nThe following error occured in accesing a WAIS server:<P>\n";
  614.     char * key;              /* pointer to keywords in URL */
  615.     char* request_message = NULL; /* arbitrary message limit */
  616.     char* response_message = NULL; /* arbitrary message limit */
  617.     long request_buffer_length;    /* how of the request is left */
  618.     SearchResponseAPDU  *retrieval_response = 0;
  619.     char keywords[MAX_KEYWORDS_LENGTH + 1];
  620.     char *server_name;    
  621.     char *wais_database = NULL;        /* name of current database */
  622.     char *www_database;            /* Same name escaped */
  623.     char *service;
  624.     char *doctype;
  625.     char *doclength;
  626.     long document_length;
  627.     char *docname;
  628.     FILE *connection = NULL;
  629.     char * names;        /* Copy of arg to be hacked up */
  630.     BOOL ok = NO;
  631.     int return_status = HT_LOADED;
  632.     int rv;
  633.     
  634.     extern FILE * connect_to_server();
  635.     
  636.     if (!acceptable_inited) init_acceptable();
  637.     
  638.         
  639. /*    Decipher and check syntax of WWW address:
  640. **    ----------------------------------------
  641. **
  642. **    First we remove the "wais:" if it was spcified.  920110
  643. */  
  644.     names = HTParse(arg, "", PARSE_HOST | PARSE_PATH | PARSE_PUNCTUATION);
  645.     key = strchr(names, '?');
  646.     
  647.     if (key) {
  648.         char * p;
  649.     *key++ = 0;    /* Split off keywords */
  650.     for (p=key; *p; p++) if (*p == '+') *p = ' ';
  651.     HTUnEscape(key);
  652.     }
  653.     if (names[0]== '/') {
  654.     server_name = names+1;
  655.     if (as_gate =(*server_name == '/'))
  656.         server_name++;    /* Accept one or two */
  657.     www_database = strchr(server_name,'/');
  658.     if (www_database) {
  659.         *www_database++ = 0;        /* Separate database name */
  660.         doctype = strchr(www_database, '/');
  661.         if (key) ok = YES;    /* Don't need doc details */
  662.         else if (doctype) {    /* If not search parse doc details */
  663.         *doctype++ = 0;    /* Separate rest of doc address */
  664.         doclength = strchr(doctype, '/');
  665.         if(doclength) {
  666.             *doclength++ = 0;
  667.             document_length = atol(doclength);
  668.             if (document_length) {
  669.             docname=strchr(doclength, '/');
  670.             if (docname) {
  671.                 *docname++ = 0;
  672.                 ok = YES;    /* To avoid a goto! */
  673.             } /* if docname */
  674.             } /* if document_length valid */
  675.         } /* if doclength */
  676.         } else { /* no doctype?  Assume index required */
  677.             if (!key) key = "";
  678.         ok = YES;
  679.         } /* if doctype */
  680.     } /* if database */
  681.      }
  682.      
  683.      if (!ok)
  684.      return HTLoadError(sink, 500, "Syntax error in WAIS URL");
  685.  
  686.      if (TRACE) fprintf(stderr, "HTWAIS: Parsed OK\n");
  687.      
  688.      service = strchr(names, ':');
  689.      if (service)  *service++ = 0;
  690.      else service = "210";
  691.      
  692.      if (server_name[0] == 0)
  693.         connection = NULL;
  694.  
  695.      else if (!(key && !*key))
  696.     {
  697.       int status;
  698.       if (TRACE)
  699.         fprintf (stderr, "===WAIS=== calling mosaic_connect_to_server\n");
  700.       status = mosaic_connect_to_server
  701.                     (server_name, atoi(service), &connection);
  702.       if (status == 0)
  703.         {
  704.           if (TRACE)
  705.             fprintf (stderr, "===WAIS=== connection failed\n");
  706.           free(names);
  707.           return HT_NOT_LOADED;
  708.         }
  709.       else if (status == -1)
  710.         {
  711.           if (TRACE)
  712.             fprintf (stderr, "===WAIS=== connection interrupted\n");
  713.           free(names);
  714.           return HT_INTERRUPTED;
  715.         }
  716.     }
  717.  
  718.     StrAllocCopy(wais_database,www_database);
  719.     HTUnEscape(wais_database);
  720.     
  721.     /* This below fixed size stuff is terrible */
  722. #ifdef VMS
  723.     if (!(request_message =
  724.           (char*)calloc((size_t)MAX_MESSAGE_LEN*sizeof(char),1)))
  725.     outofmem(__FILE__, "HTLoadWAIS");
  726.     if (!(response_message =
  727.           (char*)calloc((size_t)MAX_MESSAGE_LEN*sizeof(char),1)))
  728.     outofmem(__FILE__, "HTLoadWAIS");
  729. #else
  730.     request_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char));
  731.     response_message = (char*)s_malloc((size_t)MAX_MESSAGE_LEN * sizeof(char));
  732. #endif /* VMS */
  733.  
  734. /*    If keyword search is performed but there are no keywords,
  735. **    the user has followed a link to the index itself. It would be
  736. **    appropriate at this point to send him the .SRC file - how?
  737. */
  738.  
  739.     if (key && !*key) {                /* I N D E X */
  740.     
  741. #ifdef CACHE_FILE_PREFIX
  742.     char filename[256];
  743.     FILE * fp;
  744. #endif
  745.     HTStructured * target = HTML_new(anAnchor, format_out, sink);
  746.     
  747.     START(HTML_HEAD);
  748.     PUTS("\n");
  749.     START(HTML_ISINDEX);
  750.     PUTS("\n");
  751.  
  752.     {
  753.         START(HTML_TITLE);
  754.         PUTS(wais_database);
  755.         PUTS(" index");
  756.         END(HTML_TITLE);
  757.         PUTS("\n");
  758.         END(HTML_HEAD);
  759.         PUTS("\n");
  760.         
  761.         START(HTML_H1);
  762.         PUTS(wais_database);
  763.         END(HTML_H1);
  764.         PUTS("\n");
  765.         PUTS("This is a link for searching this index.\n");
  766.         
  767.     }
  768.     /* If we have seen a source file for this database, use that:
  769.     */
  770.  
  771. #ifdef CACHE_FILE_PREFIX
  772.     sprintf(filename, "%sWSRC-%s:%s:%.100s.txt",
  773.         CACHE_FILE_PREFIX,
  774.         server_name, service, www_database);
  775.  
  776.     fp = fopen(filename, "r");    /* Have we found this already? */
  777.     if (TRACE) fprintf(stderr,
  778.         "HTWAIS: Description of server %s %s.\n",
  779.         filename,
  780.         fp ? "exists already" : "does NOT exist!");
  781.  
  782.     if (fp) {
  783.         char c;
  784.         START(HTML_PRE);        /* Preformatted description */
  785.         while((c=getc(fp))!=EOF) PUTC(c);    /* Transfer file */
  786.         END(HTML_PRE);
  787.         fclose(fp);
  788. #endif
  789.     START(HTML_P);
  790.     PUTS("\n");
  791.     PUTS("Enter the 's'earch command and then specify search words.\n");
  792.     
  793.     FREE_TARGET;
  794.     
  795.     } else if (key) {                    /* S E A R C H */
  796.     char *p;
  797.     HTStructured * target;
  798.     
  799.     strncpy(keywords, key, MAX_KEYWORDS_LENGTH);
  800.     while(p=strchr(keywords, '+')) *p = ' ';
  801.     
  802.         /* Send advance title to get something fast to the other end */
  803.     
  804.     target = HTML_new(anAnchor, format_out, sink);
  805.     
  806.     START(HTML_HEAD);
  807.     PUTS("\n");
  808.     START(HTML_ISINDEX);
  809.     PUTS("\n");
  810.     START(HTML_TITLE);
  811.     PUTS(keywords);
  812.     PUTS(" (in ");
  813.     PUTS(wais_database);
  814.     PUTS(")");
  815.     END(HTML_TITLE);
  816.     PUTS("\n");
  817.     END(HTML_HEAD);
  818.     PUTS("\n");
  819.     
  820.     START(HTML_H1);
  821.     PUTS(keywords);
  822.     END(HTML_H1);
  823.     PUTS("\n");
  824.  
  825.     request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */
  826.     if (TRACE) fprintf(stderr, "HTWAIS: Search for `%s' in `%s'\n",
  827.         keywords, wais_database);
  828.     if(NULL ==
  829.     generate_search_apdu(request_message + HEADER_LENGTH, 
  830.                 &request_buffer_length, 
  831.                 keywords, wais_database, NULL, MAXDOCS)) {
  832. #ifdef VMS
  833.         HTAlert ("HTWAIS: Request too large.");
  834.         return_status = HT_NOT_LOADED;
  835.         FREE_TARGET;
  836.         goto CleanUp;
  837. #else
  838.         panic("request too large");
  839. #endif /* VMS */
  840.         }
  841.     
  842.     HTProgress("Searching WAIS database...");
  843.     rv = interpret_message (request_message, 
  844.                 MAX_MESSAGE_LEN - request_buffer_length, 
  845.                 response_message,
  846.                 MAX_MESSAGE_LEN,
  847.                 connection,
  848.                 false    /* true verbose */
  849.                    );
  850.  
  851.     if (rv == HT_INTERRUPTED) {
  852.         HTAlert ("Search interrupted.");
  853.         return_status = HT_INTERRUPTED;
  854.         FREE_TARGET;
  855.         goto CleanUp;
  856.     }
  857.  
  858.     else if(!rv) {
  859. #ifdef VMS
  860.         HTAlert ("HTWAIS: Return message too large.");
  861.         return_status = HT_NOT_LOADED;
  862.         FREE_TARGET;
  863.         goto CleanUp;
  864. #else
  865.         panic("returned message too large");
  866. #endif /* VMS */
  867.     
  868.         } else {    /* returned message ok */
  869.     
  870.         SearchResponseAPDU  *query_response = 0;
  871.         readSearchResponseAPDU(&query_response,
  872.             response_message + HEADER_LENGTH);
  873.         display_search_response(target, 
  874.             query_response, wais_database, keywords);
  875.         if (query_response->DatabaseDiagnosticRecords)
  876.         freeWAISSearchResponse(
  877.             query_response->DatabaseDiagnosticRecords);         
  878.         freeSearchResponseAPDU( query_response);
  879.     }    /* returned message not too large */
  880.     
  881.     FREE_TARGET;
  882.  
  883.     } else {            /* D O C U M E N T    F E T C H */
  884.     
  885.     HTFormat format_in;
  886.     boolean binary;     /* how to transfer stuff coming over */
  887.     HTStream * target;
  888.     long count;
  889.     any   doc_chunk;
  890.     any * docid = &doc_chunk;
  891.     if (TRACE) fprintf(stderr,
  892.         "HTWAIS: Retrieve document id `%s' type `%s' length %ld\n",
  893.         docname, doctype, document_length);
  894.         
  895.     format_in = 
  896.       !strcmp(doctype, "WSRC") ? HTAtom_for("application/x-wais-source") :
  897.       !strcmp(doctype, "TEXT") ? HTAtom_for("text/plain") :
  898.       !strcmp(doctype, "HTML") ? HTAtom_for("text/html") :
  899.       !strcmp(doctype, "GIF")  ? HTAtom_for("image/gif") :
  900.                             HTAtom_for("application/octet-stream");
  901.     binary = 
  902.       0 != strcmp(doctype, "WSRC") &&
  903.       0 != strcmp(doctype, "TEXT") &&
  904.       0 != strcmp(doctype, "HTML") ;
  905.  
  906.  
  907.     target = HTStreamStack(format_in, format_out, sink, anAnchor);
  908.     if (!target) return HTLoadError(sink, 500,
  909.         "Can't convert format of WAIS document");
  910. /*    Decode hex or litteral format for document ID
  911. */    
  912.     WAIS_from_WWW(docid, docname);
  913.  
  914.     
  915. /*    Loop over slices of the document
  916. */    
  917.     for(count = 0; 
  918.         count * CHARS_PER_PAGE < document_length;
  919.         count++){
  920. #ifdef VMS
  921.           char *type = NULL;
  922.       
  923.       StrAllocCopy(type, doctype);
  924. #else
  925.       char *type = s_strdup(doctype);    /* Gets freed I guess */
  926. #endif /* VMS */
  927.       request_buffer_length = MAX_MESSAGE_LEN; /* Amount left */
  928.       if (TRACE) fprintf(stderr, "HTWAIS: Slice number %ld\n", count);
  929.  
  930.             if(HTCheckForInterrupt())
  931.               {
  932.                 HTAlert ("Data transfer interrupted.");
  933.                 (*target->isa->_abort)(target, NULL);
  934. #ifdef VMS
  935.         if (type) free (type);
  936. #endif /* VMS */
  937.         return_status = HT_NOT_LOADED;
  938.         goto CleanUp;
  939.               }
  940.  
  941.       if(0 ==
  942.           generate_retrieval_apdu(request_message + HEADER_LENGTH,
  943.             &request_buffer_length, 
  944.             docid, 
  945.             CT_byte,
  946.             count * CHARS_PER_PAGE,
  947.             ((count + 1) * CHARS_PER_PAGE <= document_length ?
  948.                      (count + 1) * CHARS_PER_PAGE :
  949.                  document_length),
  950.             type,
  951.             wais_database
  952.             )) {
  953. #ifdef VMS
  954.             HTAlert ("HTWAIS: Request too long.");
  955.             return_status = HT_NOT_LOADED;
  956.             FREE_TARGET;
  957.             if (type) free (type);
  958.             if (docid->bytes) free (docid->bytes);
  959.             goto CleanUp;
  960. #else
  961.             panic("request too long");
  962. #endif /* VMS */
  963.         }
  964.       
  965.       /*    Actually do the transaction given by request_message */
  966.       HTProgress("Fetching WAIS document...");
  967.       rv = interpret_message (request_message, 
  968.                   MAX_MESSAGE_LEN - request_buffer_length, 
  969.                   response_message,
  970.                   MAX_MESSAGE_LEN,
  971.                   connection,
  972.                   false /* true verbose */    
  973.                      );
  974.  
  975.       if (rv == HT_INTERRUPTED)
  976.         {
  977.         HTAlert ("Data transfer interrupted.");
  978.         return_status = HT_INTERRUPTED;
  979.         FREE_TARGET;
  980.         if (type) free (type);
  981.         if (docid->bytes) free (docid->bytes);
  982.         goto CleanUp;
  983.         }
  984.  
  985.       else if (!rv)
  986.         {
  987. #ifdef VMS
  988.         HTAlert ("HTWAIS: Return message too large.");
  989.         return_status = HT_NOT_LOADED;
  990.         FREE_TARGET;
  991.         if (type) free (type);
  992.         if (docid->bytes) free (docid->bytes);
  993.         goto CleanUp;
  994. #else
  995.             panic("Returned message too large");
  996. #endif /* VMS */
  997.         }
  998.  
  999.       /*     Parse the result which came back into memory.
  1000.       */
  1001.       readSearchResponseAPDU(&retrieval_response, 
  1002.                  response_message + HEADER_LENGTH);
  1003.  
  1004.       if(NULL == ((WAISSearchResponse *)
  1005.           retrieval_response->DatabaseDiagnosticRecords)->Text){
  1006.         /* display_search_response(target, retrieval_response,
  1007.                     wais_database, keywords); */
  1008.         PUTS("No text was returned!\n");
  1009.         /* panic("No text was returned"); */
  1010.       } else {
  1011.       
  1012.         output_text_record(target,
  1013.            ((WAISSearchResponse *)
  1014.             retrieval_response->DatabaseDiagnosticRecords)->Text[0],
  1015.         false, binary);
  1016.       
  1017.       } /* If text existed */
  1018.       
  1019. #ifdef VMS
  1020.       if (type) free(type);
  1021. #endif /* VMS */
  1022.     }    /* Loop over slices */
  1023.  
  1024.     FREE_TARGET;
  1025.  
  1026.     if (docid->bytes) free (docid->bytes);
  1027.  
  1028.     freeWAISSearchResponse( retrieval_response->DatabaseDiagnosticRecords); 
  1029.     freeSearchResponseAPDU( retrieval_response);
  1030.  
  1031.     } /* If document rather than search */
  1032.  
  1033.  
  1034.  
  1035. CleanUp:
  1036. /*    (This postponed until later,  after a timeout:)
  1037. */
  1038. #ifdef VMS
  1039.     if (connection != NULL) NETCLOSE((int)connection);
  1040. #else
  1041.     if (connection)    close_connection(connection);
  1042. #endif /* VMS */
  1043.     if (wais_database) free(wais_database);
  1044. #ifdef VMS
  1045.     if (request_message)  free(request_message);
  1046.     if (response_message) free(response_message);
  1047. #else
  1048.     s_free(request_message);
  1049.     s_free(response_message);
  1050. #endif /* VMS */
  1051.     if (names) free(names);
  1052.     return (return_status);
  1053. }
  1054.  
  1055. GLOBALDEF PUBLIC HTProtocol HTWAIS = { "wais", HTLoadWAIS, NULL };
  1056.  
  1057.  
  1058.