home *** CD-ROM | disk | FTP | other *** search
/ Education Sampler 1992 [NeXTSTEP] / Education_1992_Sampler.iso / Programming / Source / WAIS / ir / ir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-02-02  |  23.9 KB  |  804 lines

  1. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  2.    No guarantees or restrictions.  See the readme file for the full standard
  3.    disclaimer.    
  4.   
  5. */
  6.  
  7. /*----------------------------------------------------------------------*/
  8. /* This code implements a simple Z39.50+WAIS server, which consults a 
  9.    local database using Brewster's search engine.  The main routine is
  10.    interpret_buffer() which reads the contents of a receive buffer, and 
  11.    writes results back to a send buffer.
  12.  
  13. The basic structure is:
  14.  
  15. interpret_buffer gets bytes and returns bytes from whatever transport 
  16.    mechanism. It calls either handleInit (which handles init requests)
  17.    or handleSearch.
  18.  
  19. handleSearch calls either handleRelevanceFeedbackSearch or 
  20.    handleElementRetrieval based on the type of question.
  21.  
  22. handleElementRetrieval calls getData or getDocumentText to answer that 
  23.    question.
  24.  
  25. handleRelevanceFeedbackSearch calls run_search and/or help_header to answer 
  26.    the question.
  27.  
  28.  
  29. A server must supply:
  30.   getData, getDocumentText, run_search, and help_header
  31. then it should work.
  32.  
  33.    To do:
  34.    - help facilities:
  35.      on a null query pass back random documents from the pool (?)
  36.    - Add dates to search responses
  37.  */
  38.  
  39. /* change log:
  40.  *  3/91 fixed db name defaulting for info server
  41.  *  5/31/91 fixed handleRelevanceFeedbackSearch to do search if
  42.  *          no seedwords but relevant document supplied - JG
  43.  *  5/31/91 fixed databaseName in handleElementRetrieval - HWM
  44.  *  7/19/91 fixed handleElementRetrieval prototype -BK
  45.  */
  46. /*----------------------------------------------------------------------*/
  47.  
  48. #include "ir.h"
  49. #include "wprot.h"
  50. #include "irsearch.h"
  51. #include "docid.h"
  52. #include "cutil.h"
  53. #include "irfiles.h" /* for pathname_name */
  54. #include "irretrvl.h"
  55. #include "sockets.h"  /* for connect_to_server */
  56.  
  57. #include <string.h>
  58. #include <ctype.h>
  59. #include <math.h>
  60.  
  61. #ifdef ANSI_LIKE
  62. #include <stdlib.h>
  63. #else
  64. #include "ustubs.h"
  65. #endif
  66.  
  67. static void handleInit _AP((char** recBuf,char** sendBuf,
  68.                 long* sendBufLen,
  69.                 long* maxBufLen));
  70.                        
  71. static void handleSearch _AP((char** recBuf,char** sendBuf,
  72.                   long* sendBufLen,
  73.                   long waisProtocolVersion,
  74.                   char *index_directory));
  75.  
  76. static void handleRelevanceFeedbackSearch _AP((SearchAPDU* search,
  77.                            char** sendBuf,long* sendBufLen,
  78.                            long waisProtocolVersion,
  79.                            char *index_directory));
  80.                                           
  81. static void handleElementRetrieval _AP((SearchAPDU* search,
  82.                     char** sendBuf,
  83.                     long* sendBufLen,
  84.                     long waisProtocolVersion,
  85.                     char *index_directory));
  86.                                    
  87.  
  88. /*----------------------------------------------------------------------*/
  89. /* Utility */
  90.   
  91. /*----------------------------------------------------------------------*/
  92. /* note - at present, it is not clear to me what the value of present-status
  93.    is, and how it is to be interpreted.  Furthermore, are our text retrieval
  94.    queries considered presents, or are they searches?
  95.  */
  96.  
  97.  
  98. /*----------------------------------------------------------------------*/
  99.  
  100. /* interpret_buffer()
  101. char* receiveBuffer - buffer containing data to interpret
  102. long receiveBufLen - how much data is there
  103. char* sendBuffer - buffer to write results to
  104. long sendBufLen - how much space there is to write to
  105. long* maxBufferSize - see below
  106. long waisProtocolVersion - what version of the wias protocol is in use
  107. char *index_directory - the directory to find the indexes on a search
  108.  
  109. maxBufferSize is a pointer to a per connection variable that contains the
  110. maximum size send/receive buffer to use.  Seems a lot like sendBufLen
  111. does't it.  Well it usually is, but not always.
  112.  
  113. Here is how it works from a server's point of view.  
  114.  
  115. When the client connection is first established, the server spawns a new
  116. process to deal with it.  The new process contains a global variable
  117. (bufferSize in server.c) which is initialized to BUFSZ (defined in server.c
  118. = 30000).  This is the physical size of the server's internal bufferes.
  119. Clearly that is the absolute maximum size of any z3950 message to or from
  120. this server.
  121.  
  122. So now *maxBufferSize = sendBufLen.  
  123.  
  124. Now, the first thing that a z3950 client is likely to do is send an init
  125. APDU.  The only useful thing (and it is useful) that the init APDU
  126. currently does is allow the client and server to negotiate the maxumum size
  127. of the messages that they will send.  This takes place somewhere down
  128. inside of the interpret_buffer() logic where the APDU's are decoded and
  129. response APDU's are recoded.  A pointer to bufferSize is passed to
  130. interpret_buffer() in the maxBufferSize argument, and if the buffer happens
  131. to contain an init message, bufferSize is changed (for the rest of the
  132. connection).
  133.  
  134. That is the only function maxBufferSize serves.  Note that I could have
  135. gotten rid of sendBufLen, and just used *maxBufferSize, but sendBufLen can
  136. be and does get modified by the z3950 APDU writting code, and we don't want
  137. the overall value being modified.
  138.  
  139. */
  140.  
  141. long
  142. interpret_buffer(receiveBuffer,
  143.             receiveBufLen,
  144.             sendBuffer,
  145.             sendBufLen,
  146.             maxBufferSize,
  147.             waisProtocolVersion,
  148.             index_directory)
  149. char* receiveBuffer;
  150. long receiveBufLen;
  151. char* sendBuffer;
  152. long sendBufLen;
  153. long* maxBufferSize;
  154. long waisProtocolVersion;
  155. char *index_directory;
  156. /* read & interpret receiveBuffer until receiveBufLen.  Write results into
  157.    send buffer.  Return number of bytes written - negative if there was an 
  158.    error 
  159.  */
  160. {
  161.   char* readPos = receiveBuffer;
  162.   char* writePos = sendBuffer;
  163.  
  164.   while (readPos - receiveBuffer < receiveBufLen && /* there is more to read */
  165.          writePos != NULL    /* no write error */
  166.      )
  167.     { pdu_type pdu = peekPDUType(readPos);
  168.       switch (pdu)
  169.     { case initAPDU:
  170.         handleInit(&readPos,&writePos,&sendBufLen,maxBufferSize);
  171.         break;
  172.       case searchAPDU:
  173.         handleSearch(&readPos,&writePos,&sendBufLen,
  174.              waisProtocolVersion, index_directory);
  175.         break;
  176.       default:
  177.         /* unknown APDU error */
  178.         writePos = NULL;
  179.         break;
  180.       }
  181.     }
  182.   
  183.   return(writePos - sendBuffer);
  184. }
  185.  
  186. /*----------------------------------------------------------------------*/
  187.  
  188. static void handleInit _AP((char** recBuf,char** sendBuf,
  189.                 long* sendBufLen,long* maxBufferSize));
  190.  
  191. static void
  192. handleInit(recBuf,sendBuf,sendBufLen,maxBufferSize)
  193. char** recBuf;
  194. char** sendBuf;
  195. long* sendBufLen;
  196. long* maxBufferSize;
  197. /* negotiate functionality and buffer sizes.  A session ususally begins
  198.    with one of these, but is not required to.  
  199.    NOTE - even if the server decides not to accept the client, it does
  200.    not shut down the connection.  It simply declies acceptatance, and 
  201.    waits for the client to shut down.
  202.  */
  203. {
  204.   InitAPDU* anInit = NULL;
  205.   
  206.   /* read the init - note there is no WAIS protocol extension here */
  207.   *recBuf = readInitAPDU(&anInit,*recBuf);
  208.   
  209.   if (recBuf == NULL || *recBuf == NULL)
  210.     { *sendBuf = NULL;        /* error in the read */
  211.       return;
  212.     }
  213.   else                /* respond to the init */
  214.     { InitResponseAPDU* reply = NULL;
  215.       WAISInitResponse* wais_response = NULL;
  216.       boolean connectionAccepted;
  217.      
  218.       /* negotiate services */
  219.       if (anInit->willPresent == false &&
  220.       anInit->willDelete == false)
  221.     connectionAccepted = true;
  222.       else
  223.     connectionAccepted = false;
  224.        
  225.       /* negotiate buffer sizes */
  226.       if (*maxBufferSize > anInit->MaximumRecordSize)
  227.     *maxBufferSize = anInit->MaximumRecordSize;
  228.      
  229.       if(anInit->IDAuthentication != NULL)
  230.     waislog(WLOG_LOW, WLOG_INFO, "Init message: %s", 
  231.         anInit->IDAuthentication);
  232.  
  233.       /* not much use huh? */
  234.       wais_response = makeWAISInitResponse(0L,0L,NULL,NULL,NULL,NULL); 
  235.      
  236.       reply = makeInitResponseAPDU(connectionAccepted,
  237.                    true,false,false,false,false,*maxBufferSize,
  238.                    *maxBufferSize,NULL,
  239.                    defaultImplementationID(),
  240.                    defaultImplementationName(),
  241.                    defaultImplementationVersion(),NULL,
  242.                    wais_response);
  243.  
  244.       /* write it */
  245.       *sendBuf = writeInitResponseAPDU(reply,*sendBuf,sendBufLen);
  246.      
  247.       /* free everything */
  248.       freeInitAPDU(anInit);
  249.       freeInitResponseAPDU(reply);
  250.       freeWAISInitResponse(wais_response);
  251.     }
  252. }
  253.  
  254.  
  255. /*----------------------------------------------------------------------*/
  256. static boolean
  257. isRemoteDB(db)
  258. char * db;
  259. {
  260.   return(strchr(db,'@') != NULL);
  261. }
  262.  
  263. /*----------------------------------------------------------------------*/
  264. #include "wmessage.h"
  265.  
  266. struct {
  267.   char host[256];
  268.   long port;
  269.   FILE *connection;
  270. } last_connection;
  271.  
  272. static void
  273. forwardSearch(aSearch,sendBuf,sendBufLen,waisProtocolVersion)
  274. SearchAPDU* aSearch;
  275. char** sendBuf;
  276. long* sendBufLen;
  277. long waisProtocolVersion;
  278. {
  279.   FILE *connection;
  280.   char hostname[1000], db[1000], *p, *p2;
  281.   long port, len, rlen;
  282.   char message[30000], response[30000];
  283.  
  284.   p = strchr(aSearch->DatabaseNames[0], '@');
  285.   strncpy(db, aSearch->DatabaseNames[0], p-aSearch->DatabaseNames[0]);
  286.   db[p-aSearch->DatabaseNames[0]] = 0;
  287.   p2 = strchr(p+1, ':');
  288.   if(p2 == NULL) {
  289.     strcpy(hostname, p+1);
  290.     port = 210;
  291.   }
  292.   else {
  293.     strncpy(hostname, p+1, p2-(p+1));
  294.     hostname[p2-(p+1)] = 0;
  295.     port = atoi(p2+1);
  296.   }
  297.  
  298.   strcpy(aSearch->DatabaseNames[0], db);
  299.   rlen = len = 30000;
  300.   writeSearchAPDU(aSearch, message+HEADER_LENGTH, &len);
  301.   len = 30000-len;
  302.   if(hostname[0] != 0) {
  303.     if(strcmp(hostname, last_connection.host) == 0 &&
  304.        port == last_connection.port)
  305.       connection = last_connection.connection;
  306.     else {
  307.       if (last_connection.connection != NULL)
  308.     close_connection(last_connection.connection);
  309.       strcpy(last_connection.host, hostname);
  310.       last_connection.port = port;
  311.       last_connection.connection = (FILE*)connect_to_server(hostname, port);
  312.       connection = last_connection.connection;
  313.       if(connection != NULL) {
  314.     char userInfo[500], hostname[80], init_message[1000];
  315.  
  316.     gethostname(hostname, 80);
  317. #ifdef TELL_USER
  318.     sprintf(userInfo, "server forwarding %s, from host: %s, user: %s",
  319.         VERSION, hostname, getenv("USER"));
  320. #else
  321.     sprintf(userInfo, "server forwarding %s, from host: %s", VERSION, hostname);
  322. #endif
  323.  
  324.     init_connection(init_message, response,
  325.             1000L,
  326.             connection,
  327.             userInfo);
  328.       }
  329.     }
  330.     if(connection != NULL)
  331.       {
  332.     len = interpret_message(message, len,
  333.                 response, rlen,
  334.                 connection, false);
  335.       }
  336.     else {
  337.       static diagnosticRecord* diags[2] = {NULL, NULL};
  338.       SearchResponseAPDU* response = NULL;
  339.       WAISSearchResponse* wais_response = NULL;
  340.       char message[255];
  341.  
  342.       sprintf(message, "Database not available: %s@%s:%d.",
  343.           db, last_connection.host, last_connection.port);
  344.       diags[0] = makeDiag(true,D_RecordNotAuthorizedToBeSent,
  345.               message);
  346.  
  347.       wais_response = makeWAISSearchResponse(NULL,NULL,NULL,
  348.                          NULL,NULL,NULL,NULL,diags);
  349.       response = makeSearchResponseAPDU(0L,0L,
  350.                     1L,
  351.                     0L,UNUSED,FAILURE,
  352.                     aSearch->ReferenceID, wais_response);
  353.       *sendBuf = writeSearchResponseAPDU(response,*sendBuf,sendBufLen);
  354.  
  355.       freeSearchResponseAPDU(response);
  356.       freeWAISSearchResponse(wais_response);
  357.       waislog(WLOG_HIGH, WLOG_ERROR, message);
  358.       return;
  359.     }
  360.   }
  361.   else
  362.     len = interpret_message(message, len,
  363.                 response, rlen,
  364.                 NULL, false);
  365.   bcopy(response+HEADER_LENGTH, *sendBuf, len);
  366.   *sendBuf+=len;
  367. }
  368.  
  369. /*----------------------------------------------------------------------*/
  370.  
  371. static void handleSearch _AP((char** recBuf,char** sendBuf,
  372.                   long* sendBufLen,long waisProtocolVersion,
  373.                   char *index_directory));
  374.  
  375. static void
  376. handleSearch(recBuf,sendBuf,sendBufLen,waisProtocolVersion,index_directory)
  377. char** recBuf;
  378. char** sendBuf;
  379. long* sendBufLen;
  380. long waisProtocolVersion;
  381. char *index_directory;
  382. /* figure out what kind of search this is, (query or retrieval) and
  383.    dispatch to the appropriate function 
  384.  */
  385. {
  386.   SearchAPDU* aSearch = NULL;
  387.  
  388.   /* read the search data */
  389.   *recBuf = readSearchAPDU(&aSearch,*recBuf);
  390.  
  391.   if (*recBuf == NULL)
  392.     { *sendBuf = NULL;        /* error in the read */
  393.       return;
  394.     }
  395.   else
  396.     {                /* dispatch on the query type */
  397.       if((aSearch->DatabaseNames != NULL) &&
  398.      (aSearch->DatabaseNames[0] != NULL) &&
  399.      isRemoteDB(aSearch->DatabaseNames[0]))
  400.     forwardSearch(aSearch,sendBuf,sendBufLen,waisProtocolVersion);
  401.       else {
  402.     if (strcmp(aSearch->QueryType,QT_TextRetrievalQuery) == 0) {
  403.       handleElementRetrieval(aSearch,sendBuf,sendBufLen,
  404.                  waisProtocolVersion, index_directory);
  405.     }
  406.     else if (strcmp(aSearch->QueryType,QT_RelevanceFeedbackQuery) == 0) {
  407.       char *seeds, *s;
  408.  
  409.       s = seeds = s_strdup(((WAISSearch *)aSearch->Query)->SeedWords);
  410.       while(*s != 0) {
  411.         if(*s == '\n' || *s == '\r') *s = ' ';
  412.         s++;
  413.       }
  414.  
  415.       if(aSearch->DatabaseNames != NULL &&
  416.          aSearch->DatabaseNames[0] != NULL)
  417.         waislog(WLOG_LOW, WLOG_SEARCH,
  418.             "Search! Database: %s, Seed Words: %s", 
  419.             aSearch->DatabaseNames[0], 
  420.             seeds);
  421.       else
  422.         waislog(WLOG_LOW, WLOG_SEARCH, 
  423.             "Search! Database: None, Seed Words: %s", 
  424.             seeds);
  425.  
  426.       handleRelevanceFeedbackSearch(aSearch,sendBuf,sendBufLen,
  427.                     waisProtocolVersion,
  428.                     index_directory);
  429.     }
  430.     else {
  431.       waislog(WLOG_HIGH, WLOG_ERROR, "Unknown search type");
  432.       *sendBuf = NULL;    /* error - unknown search type */
  433.     }
  434.     fflush(stderr);
  435.       }
  436.     }
  437. }
  438.  
  439.  
  440.      
  441.  
  442. /*----------------------------------------------------------------------*/
  443.  
  444. static boolean needs_help _AP ((char *question));
  445.  
  446. static boolean needs_help(question)
  447. char *question;
  448. /* returns true if the user wants help */
  449. {
  450.   if(question[0] == '\0')  /* null question, must need help */
  451.     return(true);
  452.   if(question[0] == '?')
  453.     return(true);
  454.   if(strlen(question) < 20){
  455.     if((NULL != strstr(question, "help")) ||
  456.        (NULL != strstr(question, "HELP")) ||
  457.        (NULL != strstr(question, "Help"))){
  458.       return(true);
  459.     }      
  460.   }
  461.   return(false);
  462. }
  463.  
  464. /* returns a help header to be returned or NULL if not possible */
  465. static WAISDocumentHeader *help_header _AP((char *database_name, 
  466.                         char *index_directory));
  467. static WAISDocumentHeader *help_header(database_name, index_directory)
  468.      char *database_name;
  469.      char *index_directory;
  470. {
  471.   /* make a help document */
  472.   hit help;
  473.   char local_id[MAX_FILENAME_LEN + 60];
  474.  
  475.   strncpy(help.filename,
  476.       merge_pathnames(database_name,index_directory), 
  477.       MAX_FILENAME_LEN);
  478.   strncat(help.filename, source_ext, MAX_FILENAME_LEN);
  479.   /* printf("help filename %s", help.filename); */
  480.  
  481.   strncpy(help.headline, "Information on database: ", MAX_FILENAME_LEN);
  482.   strncat(help.headline, pathname_name(database_name), 
  483.       MAX_FILENAME_LEN);
  484.   sprintf(local_id, "%ld %ld %s", 0L, 0L, help.filename);
  485.  
  486.   if(probe_file(help.filename))
  487.     { 
  488.       DocID* theDocID = NULL;
  489.       long length;
  490.       long lines;
  491.       char **type = NULL;
  492.  
  493.       help.start_character = 0;
  494.       help.end_character = 0;
  495.     
  496.       { FILE *stream = s_fopen(help.filename, "r");
  497.     lines = count_lines(stream);
  498.     length = file_length(stream);
  499.         s_fclose(stream);
  500.       }
  501.  
  502.       type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  503.       type[0] = s_strdup("WSRC");
  504.       type[1] = NULL;
  505.  
  506.       /* then there is a source structure to return */
  507.       theDocID = makeDocID();
  508.       theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  509.       theDocID->originalLocalID = stringToAny(local_id);
  510.  
  511.       return(makeWAISDocumentHeader(anyFromDocID(theDocID),
  512.                     UNUSED,
  513.                     MAX_NORMAL_SCORE,
  514.                     UNUSED,
  515.                     length,lines,
  516.                     type,
  517.                     s_strdup(database_name), /* XXX */
  518.                     NULL, /* date */
  519.                     s_strdup(help.headline),
  520.                     NULL));
  521.     }    
  522.   else 
  523.     return(NULL);
  524. }
  525.  
  526.  
  527.  
  528.  
  529.  
  530.  
  531. /* picks a set of random documents from the database 
  532. static void pick_random_documents(aSearch, headers, &headerNum)
  533. {
  534.   
  535. }
  536. */
  537.  
  538.  
  539. /*----------------------------------------------------------------------*/
  540.  
  541. static void handleRelevanceFeedbackSearch 
  542.   _AP((SearchAPDU* aSearch,char** sendBuf,long* sendBufLen,
  543.        long waisProtocolVersion,
  544.        char *index_directory));
  545.  
  546. static void
  547. handleRelevanceFeedbackSearch(aSearch,sendBuf,sendBufLen,
  548.                   waisProtocolVersion,
  549.                   index_directory)
  550. SearchAPDU* aSearch;
  551. char** sendBuf;
  552. long* sendBufLen;
  553. long waisProtocolVersion;
  554. char *index_directory;
  555.   SearchResponseAPDU* response = NULL;
  556.   WAISSearchResponse* wais_response = NULL;
  557.  
  558.   WAISDocumentHeader** headers = NULL;
  559.   long headerNum = 0;
  560.   char* seedwords_used = NULL;
  561.   diagnosticRecord** diags = NULL;
  562.   char *seed_words_used = s_strdup(((WAISSearch *)aSearch->Query)->SeedWords);
  563.   boolean search_status;
  564.  
  565.   /* construct a response list */
  566.   headers = (WAISDocumentHeader**)
  567.     s_malloc((size_t)
  568.          (sizeof(WAISDocumentHeader*) * 
  569.           (1 + (unsigned long)
  570.            ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved)));
  571.   headers[0] = NULL;
  572.   
  573.   /* handle help queries */
  574.   if(needs_help(seed_words_used)){
  575.     char *database_name = (aSearch->DatabaseNames == NULL) ?
  576.       INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  577.  
  578.     WAISDocumentHeader *header = help_header(database_name, index_directory);
  579.     if(NULL != header){
  580.       headers[headerNum++] = header;
  581.       headers[headerNum] = NULL;    
  582.     }
  583.   }  
  584.   if(seed_words_used[0] == '\0' &&
  585.      ((WAISSearch *)aSearch->Query)->Docs == NULL){
  586.     /* pick_random_documents(aSearch, headers, &headerNum); */
  587.   }
  588.   else
  589.     /* run the search on the database.  If a new
  590.        search engine were to be used, this is where it would be hooked in */
  591.     search_status = run_search(aSearch, headers,&diags, index_directory, 
  592.                    &seed_words_used, waisProtocolVersion,
  593.                    &headerNum);
  594.  
  595.   { /* generate report on results. */
  596.     char *message;
  597.     long size, i;
  598.  
  599.     /* calculate total length needed for log report */
  600.     for(size = 0L, i = 0; i < headerNum; i++) 
  601.       size+=(headers[i]->DocumentID->size+2);
  602.     if (size > 0) {
  603.       message = s_malloc(size);
  604.       message[0] = 0;
  605.  
  606.       for (i = 0; i < headerNum; i++) {
  607.     char docname[MAX_FILE_NAME_LEN+50];
  608.     sprintf(docname, "%s", 
  609.         anyToString(GetLocalID(docIDFromAny(headers[i]->DocumentID))));
  610.     s_strncat(message, docname, headers[i]->DocumentID->size, size);
  611.     if ( i < headerNum-1)
  612.       strcat(message, ", ");
  613.       }
  614.       waislog(WLOG_LOW, WLOG_RESULTS,
  615.           "Returned %d results: %s", headerNum, message);
  616.       s_free(message);
  617.     }
  618.     else
  619.       waislog(WLOG_LOW, WLOG_RESULTS,
  620.           "Returned 0 results.  Aww.");
  621.   }    
  622.   wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  623.                                          NULL,NULL,NULL,NULL,diags);
  624.   response = makeSearchResponseAPDU(search_status,0L,
  625.                     headerNum + ((diags == NULL) ? 0 : 1),
  626.                     0L,UNUSED,SUCCESS,
  627.                                     aSearch->ReferenceID, wais_response);
  628.   
  629.   /* write it */
  630.   *sendBuf = writeSearchResponseAPDU(response,*sendBuf,sendBufLen);
  631.  
  632.   freeWAISSearch((WAISSearch*)aSearch->Query); 
  633.   freeSearchAPDU(aSearch);
  634.   freeSearchResponseAPDU(response);
  635.   freeWAISSearchResponse(wais_response); /* free headers & seed_words_used */
  636. }
  637.  
  638. /*----------------------------------------------------------------------*/
  639.  
  640. /* this is defined above  -brewster
  641. static void handleElementRetrieval
  642.   _AP((SearchAPDU* aSearch,char** sendBuf,long* sendBufLen,
  643.        long waisProtocolVersion));
  644. */
  645.  
  646. static void 
  647. handleElementRetrieval(aSearch,sendBuf,sendBufLen,waisProtocolVersion, index_directory)
  648. SearchAPDU* aSearch;
  649. char** sendBuf;
  650. long* sendBufLen;
  651. long waisProtocolVersion;
  652. char *index_directory;
  653. /* this is a type 1 query of the restricted form specified in the 
  654.    WAIS-protocol.  Interpret it and write out an appropriate search
  655.    response. (note the valid element sets are Document-Text,Document-Headlines,
  656.    and Document-Codes but we only support text for now).
  657.  */
  658.   SearchResponseAPDU* response = NULL;
  659.   WAISSearchResponse* wais_response = NULL;
  660.   DocObj** docs = NULL;
  661.   DocObj* doc = NULL;
  662.   char *databaseName;
  663.   void **elementList = NULL;
  664.   void *element = NULL;
  665.   diagnosticRecord** diags = NULL;
  666.   diagnosticRecord* diag = NULL;
  667.   long numDiags = 0L;
  668.   long numElements = 0L;
  669.   long i;
  670.   database* db;
  671.   char* new_db_name;
  672.   
  673.   /* read the query */
  674.   docs = readWAISTextQuery((any*)aSearch->Query);
  675.   databaseName = (aSearch->DatabaseNames == NULL) ?
  676.     INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  677.  
  678.   new_db_name = merge_pathnames(databaseName, index_directory);
  679.  
  680.   /* assemble the elements and construct a response */
  681.   for (i = 0L, doc = docs[i]; doc != NULL; doc = docs[++i])
  682.    { 
  683.      long errorCode;
  684.      any* bufAny;
  685.      long size;
  686.  
  687.      if (doc->Type != NULL &&
  688.      strcmp(doc->Type, "WAIS_NEXT") == 0) {
  689.        char docname[MAX_FILE_NAME_LEN+50], *buffer;
  690.  
  691.        db = openDatabase(new_db_name, false, true);
  692.        if ((size = 
  693.         next_doc(docname, 
  694.              anyToString(GetLocalID(docIDFromAny(doc->DocumentID))),
  695.              db))
  696.        > 0) {
  697.      buffer = s_malloc(strlen(docname)+50);
  698.      sprintf(buffer, "%s, %d", docname, size);
  699.      bufAny = makeAny(strlen(buffer)+1,buffer);
  700.      element = (void*)makeWAISDocumentText(duplicateAny(doc->DocumentID),0L,bufAny);
  701.        }
  702.        else element = NULL;
  703.        closeDatabase(db);
  704.      }
  705.      else if (doc->Type != NULL &&
  706.           strcmp(doc->Type, "WAIS_PREV") == 0) {
  707.        char docname[MAX_FILE_NAME_LEN+50], *buffer;
  708.        any* bufAny;
  709.        long size;
  710.  
  711.        db = openDatabase(new_db_name, false, true);
  712.        if ((size = 
  713.         previous_doc(docname, 
  714.              anyToString(GetLocalID(docIDFromAny(doc->DocumentID))),
  715.              db))
  716.        > 0) {
  717.      buffer = s_malloc(strlen(docname)+50);
  718.      sprintf(buffer, "%s, %d", docname, size);
  719.      bufAny = makeAny(strlen(buffer),buffer);
  720.      element = (void*)makeWAISDocumentText(duplicateAny(doc->DocumentID),0L,bufAny);
  721.        }
  722.        else element = NULL;
  723.        closeDatabase(db);
  724.      }
  725.      else if (doc->ChunkCode == CT_line)
  726.        element = (void*)getDocumentText(doc, databaseName, &errorCode);
  727.      else if (doc->ChunkCode == CT_byte)
  728.        element = (void*)getData(doc, databaseName, &errorCode);
  729.  
  730.      if (element == NULL)
  731.        {            /* make a diagnostic record to return */
  732.      switch (errorCode)
  733.        { case GDT_UnsupportedChunkType:
  734.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  735.                    "Bad ChunkType in Request");
  736.            break;
  737.          case GDT_BadDocID:
  738.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  739.                    "Bad DocID in request");
  740.            break;
  741.          case GDT_MissingDocID:
  742.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  743.                    "Missing DocID in request");
  744.            break;
  745.          case GDT_BadRange:
  746.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  747.                    "Request out of range");
  748.            break;
  749.          case GDT_MissingDatabase:
  750.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  751.                    "Database missing from request");
  752.            break;
  753.          case GDT_BadDatabase:
  754.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  755.                    "File not present in specified database");
  756.            break;
  757.          default:
  758.            /* should never get here */
  759.            diag = NULL;
  760.            break;
  761.          };
  762.      diags = (diagnosticRecord**)s_realloc(diags,(size_t)(sizeof(diagnosticRecord*) * 
  763.                                   (numDiags + 2)));
  764.      diags[numDiags++] = diag;
  765.      diags[numDiags] = NULL;
  766.        }
  767.      else 
  768.        { if (elementList == NULL) /* create a list */
  769.        { elementList = (void**)s_malloc((size_t)sizeof(void*) * 2);
  770.        }
  771.        else            /* grow the list */
  772.      { elementList = (void**)s_realloc((char*)elementList,
  773.                        (size_t)(sizeof(void*) * 
  774.                             (numElements + 2)));
  775.      }
  776.        elementList[numElements++] = element; /* put it in the list */
  777.        elementList[numElements] = NULL;
  778.        }
  779.    }
  780.  
  781.   wais_response = makeWAISSearchResponse(NULL,NULL,NULL,NULL,
  782.                                          (WAISDocumentText**)elementList,NULL,
  783.                                          NULL,diags);
  784.   response = makeSearchResponseAPDU(SUCCESS,0L,numElements + numDiags,0L,UNUSED,
  785.                                     SUCCESS,aSearch->ReferenceID,
  786.                     wais_response);
  787.   
  788.   /* write it */
  789.   *sendBuf = writeSearchResponseAPDU(response,*sendBuf,sendBufLen);
  790.   
  791.   /* clean up */
  792.   freeAny((any*)aSearch->Query); /* have to explicitly free the user info */
  793.   freeSearchAPDU(aSearch);
  794.   freeSearchResponseAPDU(response);
  795.   freeWAISSearchResponse(wais_response); /* frees the elements constructed */
  796.   doList((void**)docs,freeDocObj);
  797.   s_free(docs);  
  798. }
  799.  
  800. /*----------------------------------------------------------------------*/
  801.  
  802.