home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 5 / Skunkware 5.iso / src / Tools / freeWAIS-sf-1.1 / ir / ir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-13  |  49.3 KB  |  1,697 lines

  1.  
  2. /* Copyright (c) CNIDR (Work in progress) */
  3.  
  4. /* WIDE AREA INFORMATION SERVER SOFTWARE:
  5.    No guarantees or restrictions.  See the readme file for the full standard
  6.    disclaimer.    
  7.   
  8. */
  9.  
  10. /* Change log:
  11.  * $Log: ir.c,v $
  12.  * Revision 1.13  1994/12/13  18:52:45  pfeifer
  13.  * chip@chinacat.unicom.com (Chip Rosenthal) patches.
  14.  * Excluding the merge of libinv and libwais
  15.  *
  16.  * Revision 1.12  1994/08/15  16:17:50  pfeifer
  17.  * fixed unchecked usage of getenv("USER") in printf
  18.  *
  19.  * Revision 1.11  1994/08/05  07:12:05  pfeifer
  20.  * Release beta 04
  21.  *
  22.  * Revision 1.10  1994/04/29  23:23:23  pfeifer
  23.  * Fixed getwd insted of getcwd
  24.  * ifdef'ed alphasort for gcc 2.4.3
  25.  *
  26.  * Revision 1.9  1994/04/06  23:50:22  pfeifer
  27.  * 08
  28.  *
  29.  * Revision 1.8  1994/03/21  17:29:49  huynh1
  30.  * openDatabase modified.
  31.  * Patchlevel 07.
  32.  *
  33.  * Revision 1.7  1994/03/08  20:55:03  pfeifer
  34.  * Patchlevel 04
  35.  *
  36.  * Revision 1.6  93/07/23  02:18:37  warnock
  37.  * eliminated extra argument in call to probe_file in server_security
  38.  * 
  39.  * Revision 1.5  93/07/02  17:49:54  warnock
  40.  * drop-in replacement for handleElementRetrieval from francois
  41.  * 
  42.  * Revision 1.4  93/07/01  20:33:57  warnock
  43.  * Drop-in replacement for server_security routine
  44.  * 
  45.  * Revision 1.3  93/07/01  19:16:39  warnock
  46.  * gethostname -> mygethostname
  47.  * 
  48.  * Revision 1.2  93/07/01  19:01:13  warnock
  49.  * Fix from tovio@sage.ucs.uwa.edu.au when database name array was empty
  50.  * This is Francois' drop-in replacement for ir.c
  51.  * 
  52.  * Revision 1.1  1993/02/16  15:05:35  freewais
  53.  * Initial revision
  54.  *
  55.  * Revision 1.49  92/05/10  14:43:35  jonathan
  56.  * Made a little safer on NULL docid's when parsing.
  57.  * 
  58.  * Revision 1.48  92/05/05  14:56:33  jonathan
  59.  * Added definition of S_ISDIR for Mach and NeXT.  Fixed isdoctype macro to
  60.  * check for NULLs.
  61.  * 
  62.  * Revision 1.47  92/05/04  11:28:26  jonathan
  63.  * Changed logging result list to use log_level a little better.
  64.  * 
  65.  * Revision 1.46  92/04/30  12:24:45  jonathan
  66.  * split =* for ULTRIX CC.
  67.  * changed a couple of s_free's to free's for ULTRIX CC too.
  68.  * 
  69.  * Revision 1.45  92/04/01  17:08:14  jonathan
  70.  * Added code to handle FTP-like searches.
  71.  * 
  72.  * Revision 1.44  92/03/23  13:25:22  shen
  73.  * only print out the number of the results but not the message in the log
  74.  * 
  75.  * Revision 1.43  92/03/18  08:55:45  jonathan
  76.  * Removed databaseName argument to getDocumentText and getData.
  77.  * 
  78.  * Revision 1.42  92/03/05  07:07:20  shen
  79.  * add two more dummy arguments to call to init_search_engine
  80.  * 
  81.  * Revision 1.41  92/02/23  10:37:53  jonathan
  82.  * enforced limit on results in handleRelevanceFeedbackSearch (particularly
  83.  * for handline help queries).
  84.  * 
  85.  * Revision 1.40  92/02/23  09:57:57  jonathan
  86.  * Prevent return of help messages if query is empty but there are relevant
  87.  * documents.
  88.  * 
  89.  * Revision 1.39  92/02/21  11:07:25  jonathan
  90.  * Added RCSIdent.
  91.  * 
  92.  * Revision 1.38  92/02/21  11:00:10  jonathan
  93.  * Changed logging of init message to WLOG_MEDIUM.
  94.  * 
  95.  * Revision 1.37  92/02/19  16:56:48  jonathan
  96.  * mucked a bit with the no-results case.
  97.  * 
  98.  * Revision 1.36  92/02/19  13:55:22  jonathan
  99.  * Return catalog as result if no hits to search.
  100.  * Plug some more memory leaks.
  101.  * 
  102.  * Revision 1.35  92/02/19  10:39:27  jonathan
  103.  * Off by one in last fix (headerNum>=0).  Fixed it.
  104.  * 
  105.  * Revision 1.34  92/02/19  10:16:04  jonathan
  106.  * Added code to handle too many headers for buffer.  Reduces the number of
  107.  * headers until they fit, and marks the log with a warning.
  108.  * 
  109.  * Revision 1.33  92/02/17  12:40:04  jonathan
  110.  * Return catalog as well as source description for help queries.
  111.  * 
  112.  * 
  113.  * Revision 1.32  92/02/12  13:20:49  jonathan
  114.  * Added "$Log" so RCS will put the log message in the header
  115.  * 
  116. */
  117.  
  118. #ifndef lint
  119. static char *RCSid = "$Header: /usr/local/ls6/src+data/src/freeWAIS-sf/ir/RCS/ir.c,v 1.13 1994/12/13 18:52:45 pfeifer Exp $";
  120. #endif
  121.  
  122. /*----------------------------------------------------------------------*/
  123. /* This code implements a simple Z39.50+WAIS server, which consults a 
  124.    local database using Brewster's search engine.  The main routine is
  125.    interpret_buffer() which reads the contents of a receive buffer, and 
  126.    writes results back to a send buffer.
  127.  
  128. The basic structure is:
  129.  
  130. interpret_buffer gets bytes and returns bytes from whatever transport 
  131.    mechanism. It calls either handleInit (which handles init requests)
  132.    or handleSearch.
  133.  
  134. handleSearch calls either handleRelevanceFeedbackSearch or 
  135.    handleElementRetrieval based on the type of question.
  136.  
  137. handleElementRetrieval calls getData or getDocumentText to answer that 
  138.    question.
  139.  
  140. handleRelevanceFeedbackSearch calls run_search and/or help_header to answer 
  141.    the question.
  142.  
  143.  
  144. A server must supply:
  145.   getData, getDocumentText, run_search, and help_header
  146. then it should work.
  147.  
  148.    To do:
  149.    - help facilities:
  150.      on a null query pass back random documents from the pool (?)
  151.    - Add dates to search responses
  152.  */
  153.  
  154. /* change log:
  155.  *  3/91 fixed db name defaulting for info server
  156.  *  5/31/91 fixed handleRelevanceFeedbackSearch to do search if
  157.  *          no seedwords but relevant document supplied - JG
  158.  *  5/31/91 fixed databaseName in handleElementRetrieval - HWM
  159.  *  7/19/91 fixed handleElementRetrieval prototype -BK
  160.  */
  161. /*----------------------------------------------------------------------*/
  162.  
  163. #include "server.h"
  164. #include "ir.h"
  165. #include "wprot.h"
  166. #include "irsearch.h"
  167. #include "docid.h"
  168. #include "cutil.h"
  169. #include "irfiles.h" /* for pathname_name */
  170. #include "irretrvl.h"
  171. #include "sockets.h"  /* for connect_to_server */
  172. #include "panic.h"
  173.  
  174. #include <ctype.h>
  175. /*
  176. #include <string.h>
  177. #include <math.h>
  178.  
  179. #ifdef ANSI_LIKE
  180. #include <stdlib.h>
  181. #else
  182. #include "ustubs.h"
  183. #endif
  184. */
  185. #include <sys/stat.h>
  186. #include "irdirent.h"
  187.  
  188. /* forward declarations */
  189.  
  190. static boolean needs_help _AP ((char *question));
  191.  
  192. static WAISDocumentHeader *help_header _AP((char *database_name, 
  193.                         char *index_directory));
  194.  
  195. static WAISDocumentHeader *catalog_header _AP((char *database_name, 
  196.                            char *index_directory,
  197.                            boolean results));
  198.  
  199. static void handleInit _AP((char** recBuf,char** sendBuf,
  200.                 long* sendBufLen,
  201.                 long* maxBufLen));
  202.                        
  203. static void handleSearch _AP((char** recBuf,char** sendBuf,
  204.                   long* sendBufLen,
  205.                   long waisProtocolVersion,
  206.                   char *index_directory));
  207.  
  208. static void handleRelevanceFeedbackSearch _AP((SearchAPDU* search,
  209.                            char** sendBuf,long* sendBufLen,
  210.                            long waisProtocolVersion,
  211.                            char *index_directory));
  212.                                           
  213. static void handleElementRetrieval _AP((SearchAPDU* search,
  214.                     char** sendBuf,
  215.                     long* sendBufLen,
  216.                     long waisProtocolVersion,
  217.                     char *index_directory));
  218.                                    
  219. static void handleFTPSearch _AP((SearchAPDU* search,
  220.                  char** sendBuf,long* sendBufLen,
  221.                  long waisProtocolVersion,
  222.                  char *index_directory));
  223.  
  224. boolean server_security _AP((char *index_directory, char *database_name));
  225.  
  226. #define isdoctype(doc, doctype) \
  227.  (((doc) != NULL) && \
  228.  ((doc)->Type != NULL) && \
  229.  !strcmp((doc)->Type, doctype))
  230.  
  231. #ifdef Mach
  232. #include <sys/inode.h>
  233. #endif /* Mach */
  234.  
  235. /* francois */
  236. #ifndef S_ISDIR
  237. #define S_ISDIR(f_mode) ((f_mode & S_IFMT) == S_IFDIR)
  238. #endif
  239.  
  240. /*----------------------------------------------------------------------*/
  241. /* Utility */
  242.   
  243. /*----------------------------------------------------------------------*/
  244. /* note - at present, it is not clear to me what the value of present-status
  245.    is, and how it is to be interpreted.  Furthermore, are our text retrieval
  246.    queries considered presents, or are they searches?
  247.  */
  248.  
  249.  
  250. /*----------------------------------------------------------------------*/
  251.  
  252. /* interpret_buffer()
  253. char* receiveBuffer - buffer containing data to interpret
  254. long receiveBufLen - how much data is there
  255. char* sendBuffer - buffer to write results to
  256. long sendBufLen - how much space there is to write to
  257. long* maxBufferSize - see below
  258. long waisProtocolVersion - what version of the wias protocol is in use
  259. char *index_directory - the directory to find the indexes on a search
  260.  
  261. maxBufferSize is a pointer to a per connection variable that contains the
  262. maximum size send/receive buffer to use.  Seems a lot like sendBufLen
  263. does't it.  Well it usually is, but not always.
  264.  
  265. Here is how it works from a server's point of view.  
  266.  
  267. When the client connection is first established, the server spawns a new
  268. process to deal with it.  The new process contains a global variable
  269. (bufferSize in server.c) which is initialized to BUFSZ (defined in server.h).
  270. This is the physical size of the server's internal bufferes.
  271. Clearly that is the absolute maximum size of any z3950 message to or from
  272. this server.
  273.  
  274. So now *maxBufferSize = sendBufLen.  
  275.  
  276. Now, the first thing that a z3950 client is likely to do is send an init
  277. APDU.  The only useful thing (and it is useful) that the init APDU
  278. currently does is allow the client and server to negotiate the maxumum size
  279. of the messages that they will send.  This takes place somewhere down
  280. inside of the interpret_buffer() logic where the APDU's are decoded and
  281. response APDU's are recoded.  A pointer to bufferSize is passed to
  282. interpret_buffer() in the maxBufferSize argument, and if the buffer happens
  283. to contain an init message, bufferSize is changed (for the rest of the
  284. connection).
  285.  
  286. That is the only function maxBufferSize serves.  Note that I could have
  287. gotten rid of sendBufLen, and just used *maxBufferSize, but sendBufLen can
  288. be and does get modified by the z3950 APDU writting code, and we don't want
  289. the overall value being modified.
  290.  
  291. */
  292.  
  293. long
  294. interpret_buffer(receiveBuffer,
  295.             receiveBufLen,
  296.             sendBuffer,
  297.             sendBufLen,
  298.             maxBufferSize,
  299.             waisProtocolVersion,
  300.             index_directory)
  301. char* receiveBuffer;
  302. long receiveBufLen;
  303. char* sendBuffer;
  304. long sendBufLen;
  305. long* maxBufferSize;
  306. long waisProtocolVersion;
  307. char *index_directory;
  308. /* read & interpret receiveBuffer until receiveBufLen.  Write results into
  309.    send buffer.  Return number of bytes written - negative if there was an 
  310.    error 
  311.  */
  312. {
  313.   char* readPos = receiveBuffer;
  314.   char* writePos = sendBuffer;
  315.  
  316.   while (readPos - receiveBuffer < receiveBufLen && /* there is more to read */
  317.          writePos != NULL    /* no write error */
  318.      )
  319.     { pdu_type pdu = peekPDUType(readPos);
  320.       switch (pdu)
  321.     { case initAPDU:
  322.         handleInit(&readPos,&writePos,&sendBufLen,maxBufferSize);
  323.         break;
  324.       case searchAPDU:
  325.         handleSearch(&readPos,&writePos,&sendBufLen,
  326.              waisProtocolVersion, index_directory);
  327.         break;
  328.       default:
  329.         /* unknown APDU error */
  330.         writePos = NULL;
  331.         waislog(WLOG_HIGH, WLOG_ERROR,
  332.             "Error in interpret_message: unknown APDU type.");
  333.         break;
  334.       }
  335.     }
  336.   
  337.   if(writePos == NULL) {
  338.     waislog(WLOG_HIGH, WLOG_ERROR,
  339.         "Error in interpret_message: NULL writePos.");
  340.     return (0);
  341.   }
  342.   else return(writePos - sendBuffer);
  343. }
  344.  
  345. /*----------------------------------------------------------------------*/
  346.  
  347. static void handleInit _AP((char** recBuf,char** sendBuf,
  348.                 long* sendBufLen,long* maxBufferSize));
  349.  
  350. static void
  351. handleInit(recBuf,sendBuf,sendBufLen,maxBufferSize)
  352. char** recBuf;
  353. char** sendBuf;
  354. long* sendBufLen;
  355. long* maxBufferSize;
  356. /* negotiate functionality and buffer sizes.  A session ususally begins
  357.    with one of these, but is not required to.  
  358.    NOTE - even if the server decides not to accept the client, it does
  359.    not shut down the connection.  It simply declies acceptatance, and 
  360.    waits for the client to shut down.
  361.  */
  362. {
  363.   InitAPDU* anInit = NULL;
  364.   
  365.   /* read the init - note there is no WAIS protocol extension here */
  366.   *recBuf = readInitAPDU(&anInit,*recBuf);
  367.   
  368.   if (recBuf == NULL || *recBuf == NULL)
  369.     { *sendBuf = NULL;        /* error in the read */
  370.       return;
  371.     }
  372.   else                /* respond to the init */
  373.     { InitResponseAPDU* reply = NULL;
  374.       WAISInitResponse* wais_response = NULL;
  375.       boolean connectionAccepted;
  376.      
  377.       /* negotiate services */
  378.       if (anInit->willPresent == false &&
  379.       anInit->willDelete == false)
  380.     connectionAccepted = true;
  381.       else
  382.     connectionAccepted = false;
  383.        
  384.       /* negotiate buffer sizes */
  385.       if (*maxBufferSize > anInit->MaximumRecordSize)
  386.     *maxBufferSize = anInit->MaximumRecordSize;
  387.      
  388.       if(anInit->IDAuthentication != NULL)
  389.     waislog(WLOG_MEDIUM, WLOG_INFO, "Init message: %s", 
  390.         anInit->IDAuthentication);
  391.  
  392.       /* not much use huh? */
  393.       wais_response = makeWAISInitResponse(0L,0L,NULL,NULL,NULL,NULL); 
  394.      
  395.       reply = makeInitResponseAPDU(connectionAccepted,
  396.                    true,false,false,false,false,*maxBufferSize,
  397.                    *maxBufferSize,NULL,
  398.                    defaultImplementationID(),
  399.                    defaultImplementationName(),
  400.                    defaultImplementationVersion(),NULL,
  401.                    wais_response);
  402.  
  403.       /* write it */
  404.       *sendBuf = writeInitResponseAPDU(reply,*sendBuf,sendBufLen);
  405.      
  406.       /* free everything */
  407.       freeInitAPDU(anInit);
  408.       freeInitResponseAPDU(reply);
  409.       freeWAISInitResponse(wais_response);
  410.     }
  411. }
  412.  
  413.  
  414. /*----------------------------------------------------------------------*/
  415. static boolean
  416. isRemoteDB(db)
  417. char * db;
  418. {
  419.   return(strchr(db,'@') != NULL);
  420. }
  421.  
  422. /*----------------------------------------------------------------------*/
  423. #include "wmessage.h"
  424.  
  425. struct {
  426.   char host[256];
  427.   long port;
  428.   FILE *connection;
  429.   long buffer_size;
  430. } last_connection;
  431.  
  432. static void
  433. forwardSearch(aSearch,sendBuf,sendBufLen,waisProtocolVersion)
  434. SearchAPDU* aSearch;
  435. char** sendBuf;
  436. long* sendBufLen;
  437. long waisProtocolVersion;
  438. {
  439.   FILE *connection;
  440.   char hostname[1000], db[1000], *p, *p2;
  441.   long port, len, rlen;
  442.   char message[BUFSZ], response[BUFSZ];
  443.  
  444.   p = strchr(aSearch->DatabaseNames[0], '@');
  445.   strncpy(db, aSearch->DatabaseNames[0], p-aSearch->DatabaseNames[0]);
  446.   db[p-aSearch->DatabaseNames[0]] = 0;
  447.   p2 = strchr(p+1, ':');
  448.   if(p2 == NULL) {
  449.     strcpy(hostname, p+1);
  450.     port = 210;
  451.   }
  452.   else {
  453.     strncpy(hostname, p+1, p2-(p+1));
  454.     hostname[p2-(p+1)] = 0;
  455.     port = atoi(p2+1);
  456.   }
  457.  
  458.   strcpy(aSearch->DatabaseNames[0], db);
  459.   rlen = len = BUFSZ;
  460.   writeSearchAPDU(aSearch, message+HEADER_LENGTH, &len);
  461.   len = BUFSZ-len;
  462.   if(hostname[0] != 0) {
  463.     if(strcmp(hostname, last_connection.host) == 0 &&
  464.        port == last_connection.port)
  465.       connection = last_connection.connection;
  466.     else {
  467.       if (last_connection.connection != NULL)
  468.     close_connection(last_connection.connection);
  469.       strcpy(last_connection.host, hostname);
  470.       last_connection.port = port;
  471.       last_connection.connection = (FILE*)connect_to_server(hostname, port);
  472.       connection = last_connection.connection;
  473.       if(connection != NULL) {
  474.     char userInfo[500], hostname[80], init_message[1000];
  475.  
  476.     mygethostname(hostname, 80);
  477. #ifdef TELL_USER
  478.     sprintf(userInfo, "server forwarding %s, from host: %s, user: %s",
  479.         VERSION, hostname, getenv("USER")?getenv("USER"):"nobody");
  480. #else
  481.     sprintf(userInfo, "server forwarding %s, from host: %s", VERSION, hostname);
  482. #endif
  483.  
  484.     last_connection.buffer_size =
  485.       init_connection(init_message, response,
  486.               BUFSZ,
  487.               connection,
  488.               userInfo);
  489.       }
  490.     }
  491.     if(connection != NULL)
  492.       {
  493.     len = interpret_message(message, len,
  494.                 response, last_connection.buffer_size,
  495.                 connection, false);
  496.       }
  497.     else {
  498.       static diagnosticRecord* diags[2] = {NULL, NULL};
  499.       SearchResponseAPDU* response = NULL;
  500.       WAISSearchResponse* wais_response = NULL;
  501.       char message[255];
  502.  
  503.       sprintf(message, "Database not available: %s@%s:%d.",
  504.           db, last_connection.host, last_connection.port);
  505.       diags[0] = makeDiag(true,D_RecordNotAuthorizedToBeSent,
  506.               message);
  507.  
  508.       wais_response = makeWAISSearchResponse(NULL,NULL,NULL,
  509.                          NULL,NULL,NULL,NULL,diags);
  510.       response = makeSearchResponseAPDU(0L,0L,
  511.                     1L,
  512.                     0L,UNUSED,FAILURE,
  513.                     aSearch->ReferenceID, wais_response);
  514.       *sendBuf = writeSearchResponseAPDU(response,*sendBuf,sendBufLen);
  515.  
  516.       freeSearchResponseAPDU(response);
  517.       freeWAISSearchResponse(wais_response);
  518.       waislog(WLOG_HIGH, WLOG_ERROR, message);
  519.       return;
  520.     }
  521.   }
  522.   else
  523.     len = interpret_message(message, len,
  524.                 response, last_connection.buffer_size,
  525.                 NULL, false);
  526.   bcopy(response+HEADER_LENGTH, *sendBuf, len);
  527.   *sendBuf+=len;
  528. }
  529.  
  530. /*----------------------------------------------------------------------*/
  531.  
  532. static void
  533. handleSearch(recBuf,sendBuf,sendBufLen,waisProtocolVersion,index_directory)
  534. char** recBuf;
  535. char** sendBuf;
  536. long* sendBufLen;
  537. long waisProtocolVersion;
  538. char *index_directory;
  539. /* figure out what kind of search this is, (query or retrieval) and
  540.    dispatch to the appropriate function 
  541.  */
  542. {
  543.   SearchAPDU* aSearch = NULL;
  544.  
  545.   /* read the search data */
  546.   *recBuf = readSearchAPDU(&aSearch,*recBuf);
  547.  
  548.   if (*recBuf == NULL)
  549.     { *sendBuf = NULL;        /* error in the read */
  550.       return;
  551.     }
  552.   else
  553.     {                /* dispatch on the query type */
  554.       if((aSearch->DatabaseNames != NULL) &&
  555.      (aSearch->DatabaseNames[0] != NULL) &&
  556.      isRemoteDB(aSearch->DatabaseNames[0]))
  557.     forwardSearch(aSearch,sendBuf,sendBufLen,waisProtocolVersion);
  558.       else {
  559. /* francois - security */
  560.     /* patch from tovio@sage.ucs.uwa.edu.au to fix crash if aSearch->DatabaseNames 
  561.        is null
  562.     */
  563.         if ( (aSearch->DatabaseNames != NULL) &&
  564.         (aSearch->DatabaseNames[0] != NULL) &&
  565.         (server_security(index_directory,aSearch->DatabaseNames[0]) != true ) ) {
  566.                 
  567.                 SearchResponseAPDU* response = NULL;
  568.                 WAISSearchResponse* wais_response = NULL;
  569.  
  570.                 WAISDocumentHeader** headers = NULL;            
  571.                 diagnosticRecord** diags = NULL;
  572.                 diagnosticRecord* diag = NULL;
  573.                 DocObj** docs = NULL;
  574.                 
  575.                 
  576.                 char msg[MAX_FILENAME_LEN * 2];
  577.                     
  578.                 sprintf(msg,"Unautorized access to database: ", aSearch->DatabaseNames[0]);
  579.                 diag = makeDiag(false,D_AccessControlFailure,msg);
  580.                 diags = (diagnosticRecord **)s_malloc((size_t)(sizeof(diagnosticRecord*) * 2));
  581.                 (diags)[0] = diag;
  582.                 (diags)[1] = NULL;
  583.                   
  584.                 wais_response = makeWAISSearchResponse(NULL,NULL,NULL,NULL,
  585.                                          NULL,NULL,NULL,diags);
  586.                 response = makeSearchResponseAPDU(SUCCESS,0L,((diags == NULL) ? 0 : 1),
  587.                                     0L,UNUSED,SUCCESS,aSearch->ReferenceID, wais_response);
  588.  
  589.                 *sendBuf = writeSearchResponseAPDU(response,*sendBuf,sendBufLen);
  590.  
  591.                 if ( strcmp(aSearch->QueryType,QT_RelevanceFeedbackQuery) == 0) {
  592.                         freeWAISSearch((WAISSearch*)aSearch->Query);
  593.                         freeSearchAPDU(aSearch);
  594.                         freeSearchResponseAPDU(response);
  595.                         freeWAISSearchResponse(wais_response); 
  596.                 } 
  597.                 else if ( strcmp(aSearch->QueryType,QT_TextRetrievalQuery) == 0) {
  598.                         docs = readWAISTextQuery((any*)aSearch->Query);
  599.                         freeAny((any*)aSearch->Query);
  600.                         freeSearchAPDU(aSearch);
  601.                         freeSearchResponseAPDU(response);
  602.                         freeWAISSearchResponse(wais_response); 
  603.                         doList((void**)docs,freeDocObj);
  604.                         s_free(docs);  
  605.                 } 
  606.  
  607.                 return;
  608.         }
  609.  
  610.     if (strcmp(aSearch->QueryType,QT_TextRetrievalQuery) == 0) {
  611.       handleElementRetrieval(aSearch,sendBuf,sendBufLen,
  612.                  waisProtocolVersion, index_directory);
  613.     }
  614.     else if (strcmp(aSearch->QueryType,QT_RelevanceFeedbackQuery) == 0) {
  615.       char *seeds, *s;
  616.  
  617.       s = seeds = s_strdup(((WAISSearch *)aSearch->Query)->SeedWords);
  618.       while(*s != 0) {
  619.         if(*s == '\n' || *s == '\r') *s = ' ';
  620.         s++;
  621.       }
  622.  
  623.       if(aSearch->DatabaseNames != NULL &&
  624.          aSearch->DatabaseNames[0] != NULL)
  625.         waislog(WLOG_LOW, WLOG_SEARCH,
  626.             "Search! Database: %s, Seed Words: %s", 
  627.             aSearch->DatabaseNames[0], 
  628.             seeds);
  629.       else
  630.         waislog(WLOG_LOW, WLOG_SEARCH, 
  631.             "Search! Database: None, Seed Words: %s", 
  632.             seeds);
  633.  
  634.       handleRelevanceFeedbackSearch(aSearch,sendBuf,sendBufLen,
  635.                     waisProtocolVersion,
  636.                     index_directory);
  637.           s_free(seeds);        /* (up) */
  638.     }
  639.     else {
  640.       waislog(WLOG_HIGH, WLOG_ERROR, "Unknown search type");
  641.       *sendBuf = NULL;    /* error - unknown search type */
  642.     }
  643.     fflush(stderr);
  644.       }
  645.     }
  646. }
  647.  
  648.  
  649.      
  650.  
  651. /*----------------------------------------------------------------------*/
  652.  
  653. static boolean needs_help(question)
  654. char *question;
  655. /* returns true if the user wants help */
  656. {
  657.   if(question[0] == '\0')  /* null question, must need help */
  658.     return(true);
  659.   if(question[0] == '?')
  660.     return(true);
  661.   if(question[0] == '*')
  662.     return(true);
  663.   if(strlen(question) < 20){
  664.     if((NULL != strstr(question, "help")) ||
  665.        (NULL != strstr(question, "HELP")) ||
  666.        (NULL != strstr(question, "Help")) ||
  667.        (NULL != strstr(question, "all"))){
  668.       return(true);
  669.     }      
  670.   }
  671.   return(false);
  672. }
  673.  
  674. /* returns a help header to be returned or NULL if not possible */
  675. static WAISDocumentHeader *help_header(database_name, index_directory)
  676.      char *database_name;
  677.      char *index_directory;
  678. {
  679.   /* make a help document */
  680.   hit help;
  681.   char local_id[MAX_FILENAME_LEN + 60];
  682.  
  683.   strncpy(help.filename,
  684.       merge_pathnames(database_name,index_directory), 
  685.       MAX_FILENAME_LEN);
  686.   strncat(help.filename, source_ext, MAX_FILENAME_LEN);
  687.   /* printf("help filename %s", help.filename); */
  688.  
  689.   strncpy(help.headline, "Information on database: ", MAX_FILENAME_LEN);
  690.   strncat(help.headline, pathname_name(database_name), 
  691.       MAX_FILENAME_LEN);
  692.   sprintf(local_id, "%ld %ld %s", 0L, 0L, help.filename);
  693.  
  694.   if(probe_file(help.filename))
  695.     { 
  696.       DocID* theDocID = NULL;
  697.       long length;
  698.       long lines;
  699.       char **type = NULL;
  700.  
  701.       help.start_character = 0;
  702.       help.end_character = 0;
  703.     
  704.       { FILE *stream = s_fopen(help.filename, "r");
  705.     lines = count_lines(stream);
  706.     length = file_length(stream);
  707.         s_fclose(stream);
  708.       }
  709.  
  710.       type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  711.       type[0] = s_strdup("WSRC");
  712.       type[1] = NULL;
  713.  
  714.       /* then there is a source structure to return */
  715.       theDocID = makeDocID();
  716.       theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  717.       theDocID->originalLocalID = stringToAny(local_id);
  718.  
  719.       return(makeWAISDocumentHeader(anyFromDocID(theDocID),
  720.                     UNUSED,
  721.                     MAX_NORMAL_SCORE,
  722.                     UNUSED,
  723.                     length,lines,
  724.                     type,
  725.                     s_strdup(database_name), /* XXX */
  726.                     NULL, /* date */
  727.                     s_strdup(help.headline),
  728.                     NULL));
  729.     }    
  730.   else 
  731.     return(NULL);
  732. }
  733.  
  734. /* returns the catalog document to be returned or NULL if not possible */
  735.  
  736. static WAISDocumentHeader *catalog_header(database_name, index_directory, results)
  737.      char *database_name;
  738.      char *index_directory;
  739.      boolean results;
  740. {
  741.   /* make a help document */
  742.   hit catalog;
  743.   char local_id[MAX_FILENAME_LEN + 60];
  744.  
  745.   strncpy(catalog.filename,
  746.       merge_pathnames(database_name,index_directory), 
  747.       MAX_FILENAME_LEN);
  748.   strncat(catalog.filename, catalog_ext, MAX_FILENAME_LEN);
  749.   /* printf("catalog filename %s", catalog.filename); */
  750.  
  751.   if(results)
  752.     strncpy(catalog.headline,
  753.         "Catalog for database: ",
  754.         MAX_FILENAME_LEN);
  755.   else
  756.     strncpy(catalog.headline,
  757.         "Search produced no result. Here's the Catalog for database: ",
  758.         MAX_FILENAME_LEN);
  759.  
  760.   strncat(catalog.headline, pathname_name(database_name), 
  761.       MAX_FILENAME_LEN);
  762.   sprintf(local_id, "%ld %ld %s", 0L, 0L, catalog.filename);
  763.  
  764.   if(probe_file(catalog.filename))
  765.     { 
  766.       DocID* theDocID = NULL;
  767.       long length;
  768.       long lines;
  769.       char **type = NULL;
  770.  
  771.       catalog.start_character = 0;
  772.       catalog.end_character = 0;
  773.     
  774.       { FILE *stream = s_fopen(catalog.filename, "r");
  775.     lines = count_lines(stream);
  776.     length = file_length(stream);
  777.         s_fclose(stream);
  778.       }
  779.  
  780.       type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  781.       type[0] = s_strdup("TEXT");
  782.       type[1] = NULL;
  783.  
  784.       /* then there is a catalog structure to return */
  785.       theDocID = makeDocID();
  786.       theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  787.       theDocID->originalLocalID = stringToAny(local_id);
  788.  
  789.       return(makeWAISDocumentHeader(anyFromDocID(theDocID),
  790.                     UNUSED,
  791.                     (results ? MAX_NORMAL_SCORE:0),
  792.                     UNUSED,
  793.                     length,lines,
  794.                     type,
  795.                     s_strdup(database_name), /* XXX */
  796.                     NULL, /* date */
  797.                     s_strdup(catalog.headline),
  798.                     NULL));
  799.     }    
  800.   else 
  801.     return(NULL);
  802. }
  803.  
  804. /* picks a set of random documents from the database 
  805. static void pick_random_documents(aSearch, headers, &headerNum)
  806. {
  807.   
  808. }
  809. */
  810.  
  811.  
  812. /*----------------------------------------------------------------------*/
  813.  
  814. static void
  815. handleRelevanceFeedbackSearch(aSearch,sendBuf,sendBufLen,
  816.                   waisProtocolVersion,
  817.                   index_directory)
  818. SearchAPDU* aSearch;
  819. char** sendBuf;
  820. long* sendBufLen;
  821. long waisProtocolVersion;
  822. char *index_directory;
  823.   DocObj *doc = NULL;
  824.   SearchResponseAPDU* response = NULL;
  825.   WAISSearchResponse* wais_response = NULL;
  826.  
  827.   WAISDocumentHeader** headers = NULL;
  828.   long headerNum = 0;
  829.   long max_headers = ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved;
  830.   char* seedwords_used = NULL;
  831.   diagnosticRecord** diags = NULL;
  832.   char *seed_words_used = s_strdup(((WAISSearch *)aSearch->Query)->SeedWords);
  833.   boolean search_status;
  834.  
  835.   if(((WAISSearch *)aSearch->Query)->Docs!=NULL &&
  836.      (doc=((WAISSearch *)aSearch->Query)->Docs[0]) != NULL &&
  837.      (isdoctype(doc, "TEXT-FTP") || isdoctype(doc, "FTP-DIR"))) {
  838.     handleFTPSearch(aSearch,sendBuf,sendBufLen, waisProtocolVersion, index_directory);
  839.     return;
  840.   }
  841.  
  842.   /* construct a response list */
  843.   headers = (WAISDocumentHeader**)
  844.     s_malloc((size_t)
  845.          (sizeof(WAISDocumentHeader*) * 
  846.           (1 + max_headers)));
  847.   headers[0] = NULL;
  848.  
  849.   if(0 != init_search_engine(index_directory, false, true, 0, 0, 0))
  850.     panic("unable to initialize search engine");
  851.   
  852.   /* handle help queries */
  853.   if(seed_words_used[0] == '\0' &&
  854.      ((WAISSearch *)aSearch->Query)->Docs != NULL) {
  855.     1;
  856.   }
  857.   else if(needs_help(seed_words_used)) {
  858.       WAISDocumentHeader *header;
  859.       char *database_name = (aSearch->DatabaseNames == NULL) ?
  860.       INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  861.  
  862.     if(headerNum < max_headers) {
  863.       header = help_header(database_name, index_directory);
  864.       if(NULL != header) {
  865.     headers[headerNum++] = header;
  866.     headers[headerNum] = NULL;    
  867.     ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved--;
  868.       }
  869.     }
  870.     if(headerNum < max_headers) {
  871.       header = catalog_header(database_name, index_directory, true);
  872.       if(NULL != header){
  873.     headers[headerNum++] = header;
  874.     headers[headerNum] = NULL;    
  875.     ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved--;
  876.       }
  877.     }
  878.   }
  879.  
  880.   if(seed_words_used[0] == '\0' &&
  881.      ((WAISSearch *)aSearch->Query)->Docs == NULL)
  882.    {
  883.      /* pick_random_documents(aSearch, headers, &headerNum); */
  884.      search_status = true;
  885.    }
  886.   else
  887.    { /* run the search on the database.  If a new
  888.     search engine were to be used, this is where it would be hooked in */
  889.      search_status = run_search(aSearch, headers,&diags, index_directory, 
  890.                 &seed_words_used, waisProtocolVersion,
  891.                 &headerNum);
  892.    }
  893.  
  894. #define CATALOG_FOR_NO_RESULTS
  895. #ifdef CATALOG_FOR_NO_RESULTS
  896.   if(headerNum == 0 && headerNum < max_headers) {
  897.     char *database_name = (aSearch->DatabaseNames == NULL) ?
  898.       INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  899.     WAISDocumentHeader *header = catalog_header(database_name, index_directory, false);
  900.  
  901.     if(NULL != header){
  902.       waislog(WLOG_MEDIUM, WLOG_INFO,
  903.           "Search had no hits, returning catalog");
  904.       headers[headerNum++] = header;
  905.       headers[headerNum] = NULL;    
  906.     }
  907.   }
  908. #endif /* CATALOG_FOR_NO_RESULTS */
  909.  
  910.   wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  911.                                          NULL,NULL,NULL,NULL,diags);
  912.   response = makeSearchResponseAPDU(search_status,0L,
  913.                     headerNum + ((diags == NULL) ? 0 : 1),
  914.                     0L,UNUSED,SUCCESS,
  915.                                     aSearch->ReferenceID, wais_response);
  916.   /* write it */
  917.   {
  918.     char *buff;
  919.     long len;
  920.     boolean it_fit = true;
  921.  
  922.     while(headerNum >= 0) {
  923.       buff = *sendBuf;
  924.       len = *sendBufLen;
  925.       if ((buff = writeSearchResponseAPDU(response,buff,&len)) == NULL) {
  926.  
  927.     it_fit = false; /* didn't make it in the buffer. */
  928.     headerNum--; 
  929.     s_free(headers[headerNum]);
  930.     headers[headerNum] = NULL;
  931.  
  932.     s_free(wais_response);
  933.     wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  934.                            NULL,NULL,NULL,NULL,diags);
  935.     
  936.     freeSearchResponseAPDU(response);
  937.     response = makeSearchResponseAPDU(search_status,0L,
  938.                       headerNum + ((diags == NULL) ? 0:1),
  939.                       0L,UNUSED,SUCCESS,
  940.                       aSearch->ReferenceID, wais_response);
  941.       }
  942.       else {
  943.     break;
  944.       }
  945.     }
  946.     *sendBuf = buff;
  947.     *sendBufLen = len;
  948.     if (!it_fit) {
  949.       waislog(WLOG_HIGH, WLOG_WARNING, 
  950.           "Buffer overflow, adjusted results from %ld", 
  951.           ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved);
  952.     }
  953.   }
  954.  
  955.   { /* generate report on results. */
  956.      long i;
  957.   
  958.      waislog(WLOG_MEDIUM, WLOG_RESULTS, "Returned %d result%s.", headerNum,
  959.          headerNum == 1 ? "" : "s");
  960.      if (headerNum > 0) {
  961.        waislog(WLOG_LOW, WLOG_RESULTS, "Results:");
  962.  
  963.         for (i = 0; i < headerNum; i++) {
  964.       char docname[MAX_FILE_NAME_LEN+50];
  965.       DocID *docid = docIDFromAny(headers[i]->DocumentID);
  966.       char *docidstring = anyToString(GetLocalID(docid));
  967.      waislog(WLOG_LOW, WLOG_RESULTS, " %s", docidstring);
  968.         s_free(docidstring);    /* (up) */
  969.         freeDocID(docid);       /* (up) */
  970.         }
  971.  
  972.       }
  973.       else
  974.         waislog(WLOG_LOW, WLOG_RESULTS,
  975.           "Returned 0 results.  Aww.");
  976.   }
  977.   freeWAISSearch((WAISSearch*)aSearch->Query); 
  978.   freeSearchAPDU(aSearch);
  979.   freeSearchResponseAPDU(response);
  980.   freeWAISSearchResponse(wais_response); /* free headers & seed_words_used */
  981.   s_free(seed_words_used);    /* (up) */
  982. }
  983.  
  984.  
  985. /*----------------------------------------------------------------------*/
  986. static void 
  987. handleElementRetrieval(aSearch,sendBuf,sendBufLen,waisProtocolVersion, index_directory)
  988. SearchAPDU* aSearch;
  989. char** sendBuf;
  990. long* sendBufLen;
  991. long waisProtocolVersion;
  992. char *index_directory;
  993. /* this is a type 1 query of the restricted form specified in the 
  994.    WAIS-protocol.  Interpret it and write out an appropriate search
  995.    response. (note the valid element sets are Document-Text,Document-Headlines,
  996.    and Document-Codes but we only support text for now).
  997.  */
  998.   SearchResponseAPDU* response = NULL;
  999.   WAISSearchResponse* wais_response = NULL;
  1000.   DocObj** docs = NULL;
  1001.   DocObj* doc = NULL;
  1002.   char *databaseName;
  1003.   void **elementList = NULL;
  1004.   void *element = NULL;
  1005.   diagnosticRecord** diags = NULL;
  1006.   diagnosticRecord* diag = NULL;
  1007.   long numDiags = 0L;
  1008.   long numElements = 0L;
  1009.   long i;
  1010.   database* db;
  1011.   
  1012.   /* francois */
  1013.   char new_db_name[MAX_FILENAME_LEN * 2];
  1014.  
  1015.   
  1016.   /* read the query */
  1017.   docs = readWAISTextQuery((any*)aSearch->Query);
  1018.  
  1019.   /* francois */
  1020.  if (aSearch->DatabaseNames == NULL)
  1021.     strcpy(new_db_name,merge_pathnames(INFO_DATABASE_NAME, index_directory));
  1022.   else
  1023.     strcpy(new_db_name,merge_pathnames(aSearch->DatabaseNames[0], index_directory));
  1024.  
  1025.  
  1026.  
  1027.    /* francois - locks */
  1028.   db = openDatabase(new_db_name, false, 
  1029. #ifdef FIELDS /* tung, 3/94 */
  1030.                     true, true);
  1031. #else
  1032.   true);
  1033. #endif
  1034.  
  1035.   if (db == NULL){
  1036.     char msg[MAX_FILENAME_LEN * 2];
  1037.     strncpy(msg,"The following database is not available: ",
  1038.         MAX_FILENAME_LEN);
  1039.     s_strncat(msg,aSearch->DatabaseNames[0],MAX_FILENAME_LEN,MAX_FILENAME_LEN);
  1040.     diag = makeDiag(false,D_PermanentSystemError,msg);
  1041.     diags = (diagnosticRecord **)s_malloc((size_t)(sizeof(diagnosticRecord*) * 2));
  1042.     diags[0] = diag;
  1043.     diags[1] = NULL;
  1044.     
  1045.   }
  1046.   else {
  1047.   
  1048.  
  1049.        /* assemble the elements and construct a response */
  1050.        for (i = 0L, doc = docs[i]; doc != NULL; doc = docs[++i])
  1051.        { 
  1052.      long errorCode;
  1053.      any* bufAny;
  1054.      long size;
  1055.     
  1056.     
  1057.      if (doc->Type != NULL &&
  1058.          strcmp(doc->Type, "WAIS_NEXT") == 0) {
  1059.        char docname[MAX_FILE_NAME_LEN+50], *buffer;
  1060.       
  1061.       if ((size = 
  1062.         next_doc(docname, 
  1063.              anyToString(GetLocalID(docIDFromAny(doc->DocumentID))),
  1064.              db))
  1065.            > 0) {
  1066.          buffer = s_malloc(strlen(docname)+50);
  1067.          sprintf(buffer, "%s, %d", docname, size);
  1068.          bufAny = makeAny(strlen(buffer)+1,buffer);
  1069.          element = (void*)makeWAISDocumentText(duplicateAny(doc->DocumentID),0L,bufAny);
  1070.        }
  1071.        else element = NULL;
  1072.        closeDatabase(db);
  1073.      }
  1074.      else if (doc->Type != NULL &&
  1075.           strcmp(doc->Type, "WAIS_PREV") == 0) {
  1076.        char docname[MAX_FILE_NAME_LEN+50], *buffer;
  1077.        any* bufAny;
  1078.        long size;
  1079.     
  1080.        if ((size = 
  1081.         previous_doc(docname, 
  1082.                  anyToString(GetLocalID(docIDFromAny(doc->DocumentID))),
  1083.                  db))
  1084.            > 0) {
  1085.          buffer = s_malloc(strlen(docname)+50);
  1086.          sprintf(buffer, "%s, %d", docname, size);
  1087.          bufAny = makeAny(strlen(buffer),buffer);
  1088.          element = (void*)makeWAISDocumentText(duplicateAny(doc->DocumentID),0L,bufAny);
  1089.        }
  1090.        else element = NULL;
  1091.        closeDatabase(db);
  1092.      }
  1093.      else if (doc->ChunkCode == CT_line) {
  1094.        element = (void*)getDocumentText(doc, &errorCode, index_directory);
  1095.      }
  1096.      else if (doc->ChunkCode == CT_byte) {
  1097.        element = (void*)getData(doc, &errorCode, index_directory);
  1098.      }
  1099.     
  1100.      if (errorCode != GDT_NoError)
  1101.        {            /* make a diagnostic record to return */
  1102.          switch (errorCode)
  1103.            { case GDT_UnsupportedChunkType:
  1104.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  1105.                    "Bad ChunkType in Request");
  1106.            break;
  1107.          case GDT_BadDocID:
  1108.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  1109.                    "Bad DocID in request");
  1110.            break;
  1111.          case GDT_MissingDocID:
  1112.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  1113.                    "Missing DocID in request");
  1114.            break;
  1115.          case GDT_BadRange:
  1116.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  1117.                    "Request out of range");
  1118.            break;
  1119.          case GDT_MissingDatabase:
  1120.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  1121.                    "Database missing from request");
  1122.            break;
  1123.          case GDT_BadDatabase:
  1124.            diag = makeDiag(true,D_PresentRequestOutOfRange,
  1125.                    "File not present in specified database");
  1126.            break;
  1127.          default:
  1128.            /* should never get here */
  1129.            diag = NULL;
  1130.            break;
  1131.          };
  1132.          diags = (diagnosticRecord**)s_realloc(diags,(size_t)(sizeof(diagnosticRecord*) * 
  1133.                                   (numDiags + 2)));
  1134.          diags[numDiags++] = diag;
  1135.          diags[numDiags] = NULL;
  1136.        }
  1137.      if (element != NULL)
  1138.        { if (elementList == NULL) /* create a list */
  1139.            { elementList = (void**)s_malloc((size_t)sizeof(void*) * 2);
  1140.            }
  1141.        else            /* grow the list */
  1142.          { elementList = (void**)s_realloc((char*)elementList,
  1143.                            (size_t)(sizeof(void*) * 
  1144.                             (numElements + 2)));
  1145.          }
  1146.            elementList[numElements++] = element; /* put it in the list */
  1147.            elementList[numElements] = NULL;
  1148.            }
  1149.     }
  1150.    }
  1151.  
  1152.   wais_response = makeWAISSearchResponse(NULL,NULL,NULL,NULL,
  1153.                                          (WAISDocumentText**)elementList,NULL,
  1154.                                          NULL,diags);
  1155.   response = makeSearchResponseAPDU(SUCCESS,0L,numElements + numDiags,0L,UNUSED,
  1156.                                     SUCCESS,aSearch->ReferenceID,
  1157.                     wais_response);
  1158.   
  1159.   /* write it */
  1160.   *sendBuf = writeSearchResponseAPDU(response,*sendBuf,sendBufLen);
  1161.   
  1162.  
  1163.   /* clean up */
  1164.   freeAny((any*)aSearch->Query);/* have to explicitly free the user info */
  1165.   freeSearchAPDU(aSearch);
  1166.   freeSearchResponseAPDU(response);
  1167.   freeWAISSearchResponse(wais_response); /* frees the elements constructed */
  1168.   doList((void**)docs,freeDocObj);
  1169.   s_free(docs);  
  1170.  
  1171.   /* francois - locks */
  1172.   closeDatabase(db);
  1173. }
  1174.  
  1175.  
  1176.  
  1177. /*----------------------------------------------------------------------*/
  1178.  
  1179. static void
  1180. handleFTPSearch(aSearch,sendBuf,sendBufLen,
  1181.         waisProtocolVersion,
  1182.         index_directory)
  1183. SearchAPDU* aSearch;
  1184. char** sendBuf;
  1185. long* sendBufLen;
  1186. long waisProtocolVersion;
  1187. char *index_directory;
  1188. {
  1189.   SearchResponseAPDU* response = NULL;
  1190.   WAISSearchResponse* wais_response = NULL;
  1191.  
  1192.   DocID *t;
  1193.   WAISDocumentHeader** headers = NULL;
  1194.   long headerNum = 0;
  1195.   long max_headers = ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved;
  1196.   DocObj *doc;
  1197.   char* seedwords_used = NULL;
  1198.   diagnosticRecord** diags = NULL;
  1199.   char *seed_words_used = s_strdup(((WAISSearch *)aSearch->Query)->SeedWords);
  1200.   char *database_name = (aSearch->DatabaseNames == NULL) ?
  1201.     INFO_DATABASE_NAME : aSearch->DatabaseNames[0];
  1202.   boolean search_status;
  1203.   int i=0;
  1204.   char *local_id,*p;
  1205.   long start,end;
  1206.   char path[200];
  1207.   char mpath[200];
  1208.   int count;
  1209.  
  1210.   /* construct a response list */
  1211.   headers = (WAISDocumentHeader**)
  1212.     s_malloc((size_t)
  1213.          (sizeof(WAISDocumentHeader*) *
  1214.           (1 + max_headers)));
  1215.   headers[0] = NULL;
  1216.   for(doc=((WAISSearch *)aSearch->Query)->Docs[i]; ((WAISSearch *)aSearch->Query)->Docs[i]!=NULL;
  1217.       doc=((WAISSearch *)aSearch->Query)->Docs[++i]){
  1218.  
  1219.     t=docIDFromAny(doc->DocumentID);
  1220.     local_id = anyToString(GetLocalID(t));
  1221.     freeDocID(t);
  1222.     sscanf(local_id,"%ld %ld %s",&start,&end,path);
  1223.     if(strcmp(path, "/")) {
  1224.       p=strrchr(path,'/');
  1225.       if(p)
  1226.     *p='\0';
  1227.     }
  1228.  
  1229.     getcwd(mpath,198);           /* up: was getwd ?? */
  1230.  
  1231.     if(index_directory && !substrcmp(path, index_directory))
  1232.       strcpy(path, index_directory);
  1233.  
  1234.     chdir(path);
  1235.  
  1236.     loadFileHeaders(path,headers,&headerNum,database_name, 
  1237.             ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved,
  1238.             index_directory); 
  1239.     chdir(mpath);
  1240.   }
  1241.   /**** jim jim jim */
  1242.   wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  1243.                      NULL,NULL,NULL,NULL,diags);
  1244.   response = makeSearchResponseAPDU(search_status,0L,
  1245.                     headerNum + ((diags == NULL) ? 0 : 1),
  1246.                     0L,UNUSED,SUCCESS,
  1247.                     aSearch->ReferenceID, wais_response);
  1248.   /* write it */
  1249.   {
  1250.     char *buff;
  1251.     long len;
  1252.     boolean it_fit = true;
  1253.  
  1254.     while(headerNum >= 0) {
  1255.       buff = *sendBuf;
  1256.       len = *sendBufLen;
  1257.       if (headerNum*(sizeof(WAISDocumentHeader)+100) > len || (buff = writeSearchResponseAPDU(response,buff,&len)) == NULL) {
  1258.  
  1259.     it_fit = false;        /* didn't make it in the buffer. */
  1260.     headerNum--;
  1261.     s_free(headers[headerNum]);
  1262.     headers[headerNum] = NULL;
  1263.  
  1264.     s_free(wais_response);
  1265.     wais_response = makeWAISSearchResponse(seedwords_used,headers,NULL,
  1266.                            NULL,NULL,NULL,NULL,diags);
  1267.  
  1268.     freeSearchResponseAPDU(response);
  1269.     response = makeSearchResponseAPDU(search_status,0L,
  1270.                       headerNum + ((diags == NULL) ? 0:1),
  1271.                       0L,UNUSED,SUCCESS,
  1272.                       aSearch->ReferenceID, wais_response);
  1273.       }
  1274.       else {
  1275.     break;
  1276.       }
  1277.     }
  1278.     *sendBuf = buff;
  1279.     *sendBufLen = len;
  1280.     if (!it_fit) {
  1281.       waislog(WLOG_HIGH, WLOG_WARNING,
  1282.  
  1283.           "Buffer overflow, adjusted results from %ld",
  1284.           ((WAISSearch *)aSearch->Query)->MaxDocumentsRetrieved);
  1285.     }
  1286.   }
  1287.   {                /* generate report on results. */
  1288.     char *message;
  1289.     long size, i;
  1290.  
  1291.     /* calculate total length needed for log report */
  1292.     for(size = 0L, i = 0; i < headerNum; i++)
  1293.       size+=(headers[i]->DocumentID->size+2);
  1294.     if (size > 0) {
  1295.       message = s_malloc(size);
  1296.       message[0] = 0;
  1297.  
  1298.       for (i = 0; i < headerNum; i++) {
  1299.     char docname[MAX_FILE_NAME_LEN+50];
  1300.     DocID *docid = docIDFromAny(headers[i]->DocumentID);
  1301.     char *docidstring = anyToString(GetLocalID(docid));
  1302.  
  1303.     sprintf(docname, "%s", docidstring);
  1304.     s_strncat(message, docname, headers[i]->DocumentID->size, size);
  1305.  
  1306.     s_free(docid); 
  1307.     s_free(docidstring);
  1308.  
  1309.     if ( i < headerNum-1)
  1310.       strcat(message, ", ");
  1311.       }
  1312.       waislog(WLOG_LOW, WLOG_RESULTS,
  1313.           "Returned %d results: %s", headerNum, message);
  1314.       s_free(message);
  1315.     }
  1316.     else
  1317.       waislog(WLOG_LOW, WLOG_RESULTS,
  1318.           "Returned 0 results.  Aww.");
  1319.   }
  1320.   freeWAISSearch((WAISSearch*)aSearch->Query);
  1321.   freeSearchAPDU(aSearch);
  1322.   freeSearchResponseAPDU(response);
  1323.   freeWAISSearchResponse(wais_response); /* free headers & seed_words_used */
  1324. }
  1325.  
  1326. #ifndef HAVE_ALPHASORT
  1327. static int
  1328.   alphasort(d1, d2)
  1329. struct dirent **d1;
  1330. struct dirent **d2;
  1331. {
  1332.   return strcmp((*d1)->d_name, (*d2)->d_name);
  1333. }
  1334. #endif /* HAVE_ALPHASORT */
  1335. static int
  1336. filesonly(e)
  1337. struct dirent *e;
  1338. {
  1339.   struct stat sb;
  1340.   int val;
  1341.   val = (stat(e->d_name, &sb) >= 0 &&( (sb.st_mode & S_IFMT) == S_IFREG || (sb.st_mode & S_IFMT)==S_IFDIR));
  1342.   if((sb.st_mode & S_IFMT)==S_IFDIR){
  1343.     if(e->d_name[strlen(e->d_name)-1]=='.')
  1344.       return(0);
  1345.     strcat(e->d_name,"/");
  1346.   }
  1347.   return(val);
  1348. }
  1349.  
  1350. loadFileHeaders(path,headers, headerNum,database_name,maxf,index_directory)
  1351. char *path;
  1352. WAISDocumentHeader **headers;
  1353. long *headerNum;
  1354. char *database_name;
  1355. long maxf;
  1356. char *index_directory;
  1357. {
  1358.   register int i;
  1359.   register int j;
  1360.   int k;
  1361.   struct dirent **list;
  1362.   hit help;
  1363.   char local_id[MAX_FILENAME_LEN + 60];
  1364.   DocID* theDocID = NULL;
  1365.   struct stat sbuf;
  1366.   long length,flen;
  1367.   long lines;
  1368.   char **type = NULL;
  1369.   char *p,tmpb[200];
  1370.   int loop;
  1371.   int ch,text=1;
  1372.   FILE *fp;
  1373.  
  1374.   k = *headerNum;
  1375.   if ((i = scandir(".", &list, filesonly, alphasort)) < 0) {
  1376.     return;
  1377.   }
  1378.   if(strcmp(path, "/") &&
  1379.      (index_directory == NULL || 
  1380.       strcmp(path, index_directory))) {
  1381.     pathname_directory(path, help.filename);
  1382.     stat(help.filename,&sbuf);
  1383.     length=lines=sbuf.st_size;
  1384.     strncpy(help.headline, help.filename, MAX_FILENAME_LEN);
  1385.     sprintf(local_id, "%ld %ld %s", 0L, length, help.filename);
  1386.     theDocID = makeDocID();
  1387.     theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  1388.     theDocID->originalLocalID = stringToAny(local_id);
  1389.     type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1390.     strcpy(tmpb,help.filename);
  1391.     type[0] = s_strdup("FTP-DIR");
  1392.     type[1] = NULL;
  1393.  
  1394.     headers[k++]=makeWAISDocumentHeader(anyFromDocID(theDocID),
  1395.                     UNUSED, MAX_NORMAL_SCORE, UNUSED, length,lines, type,
  1396.                     s_strdup(database_name), /* XXX */
  1397.                     NULL, /* date */
  1398.                     s_strdup(help.headline),
  1399.                     NULL);
  1400.   }
  1401.  
  1402.   if(!strcmp(path, "/")) {
  1403.     *path = '\0';
  1404.   }
  1405.   for (j = 0; j < i; j++){
  1406.     if(k>=maxf)
  1407.       break;
  1408.     sprintf(help.filename,"%s/%s",path,list[j]->d_name);
  1409.     stat(list[j]->d_name,&sbuf);
  1410.     length=lines=sbuf.st_size;
  1411.     strncpy(help.headline, help.filename, MAX_FILENAME_LEN);
  1412.     sprintf(local_id, "%ld %ld %s", 0L, length, help.filename);
  1413.     theDocID = makeDocID();
  1414.     theDocID->originalDatabase = stringToAny(database_name); /* XXX */
  1415.     theDocID->originalLocalID = stringToAny(local_id);
  1416.     type = (char**)s_malloc((size_t)(sizeof(char*) * 2));
  1417.     strcpy(tmpb,help.filename);
  1418.     p=strrchr(tmpb,'.');
  1419.     if(p){
  1420.       ++p;
  1421.       if(!strcasecmp(p,"tar") || !strcasecmp(p,"shar"))
  1422.     text=0;
  1423.       else
  1424.     text=1;
  1425.     }
  1426.     if(text==1){
  1427.       fp=fopen(help.filename,"r");
  1428.       if(fp==NULL)
  1429.     text=0;
  1430.       else{
  1431.     fseek(fp,0L,2);
  1432.     flen=ftell(fp);
  1433.     for(loop=2; loop<100; loop++){
  1434.       fseek(fp,flen/loop,0);
  1435.       ch=fgetc(fp);
  1436.       if(ch==EOF || (!isprint(ch) && !isspace(ch)))
  1437.         text=0;
  1438.     }
  1439.     fclose(fp);
  1440.       }
  1441.     }
  1442.  
  1443.     if(S_ISDIR(sbuf.st_mode))
  1444.       type[0] = s_strdup("FTP-DIR");
  1445.     else if(text==0)
  1446.       type[0] = s_strdup("FTP");
  1447.     else
  1448.       type[0]=s_strdup("TEXT");
  1449.  
  1450.     type[1] = NULL;
  1451.  
  1452.     headers[k++]=makeWAISDocumentHeader(anyFromDocID(theDocID),
  1453.                     UNUSED, MAX_NORMAL_SCORE, 
  1454.                     UNUSED, length,lines, type,
  1455.                     s_strdup(database_name), /* XXX */
  1456.                     NULL, /* date */
  1457.                     s_strdup(help.headline),
  1458.                     NULL);
  1459.   }
  1460.   if(list != NULL) {
  1461.     for (j = 0; j < i; j++)
  1462.       if(list[j] != NULL) free((char *)list[j]);
  1463.     free((char *)list);
  1464.   }
  1465.   *headerNum = k;
  1466. }
  1467.  
  1468.  
  1469. /*---------------------------------------------------------------------------*/
  1470.  
  1471. boolean
  1472. server_security(index_directory,database_name)
  1473. char *index_directory;
  1474. char *database_name;
  1475. {
  1476.  
  1477.  
  1478. /* francois - security */
  1479.  
  1480. /* This little piece of code implements a very simple security
  1481.    scheme for wais.
  1482.  
  1483.    Every user connection is validated against a file which 
  1484.    contains a list of hosts/domains that can connect to this 
  1485.    server. If the file does not exist, then the server is 
  1486.    open to everybody.
  1487.    
  1488.    The names of the security files can be found in server.h
  1489.  
  1490.    The format of the SERVER SECURITY file ("SERV_SEC")
  1491.    is as follows:
  1492.  
  1493.    host_name    host_address
  1494.  
  1495.    or 
  1496.    
  1497.    welchlab.welch.jhu.edu    128.220.59.10
  1498.    welchlgate.welch.jhu.edu    128.220.59.13
  1499.    
  1500.    
  1501.    Access can be given to specific domains, so if one 
  1502.    wanted to give access to everyone in the 
  1503.    welch.jhu.edu domain, the file would look like this:
  1504.    
  1505.    welch.jhu.edu    128.220.59
  1506.    
  1507.    Note that the host address is optional, but that the 
  1508.    host name is not.
  1509.   
  1510.  
  1511.   
  1512.    The format of the DATABASE SECURITY file ("DATA_SEC")
  1513.    is as follows:
  1514.  
  1515.    database_name    host_name    host_address
  1516.  
  1517.    or 
  1518.    
  1519.    foo    welchlab.welch.jhu.edu        128.220.59.10
  1520.    foo    welchlgate.welch.jhu.edu    128.220.59.13
  1521.    
  1522.    
  1523.    Access can be given to specific domains, so if one 
  1524.    wanted to give access to everyone in the 
  1525.    welch.jhu.edu domain, the file would look like this:
  1526.    
  1527.    foo    welch.jhu.edu    128.220.59
  1528.    
  1529.    Note that the host address is optional, but that the 
  1530.    host name is not.
  1531.    
  1532.    Using a star in this case would allow access to everyone 
  1533.    for a particular database, for example:
  1534.  
  1535.    foo    welch.jhu.edu    128.220.59
  1536.    bar    *    *
  1537.    
  1538.    This would be useful if you wanted to give public access
  1539.    to certain databases on a particular server and restricted 
  1540.    access to others.
  1541.    
  1542. */
  1543.    
  1544.  
  1545.  
  1546.     FILE *   security_stream = NULL;
  1547.     char     security_filename[MAX_FILE_NAME_LEN+50];
  1548.     char     security_line[100];
  1549.     char     security_database_name[100];
  1550.     char     security_host_name[100];
  1551.     char     security_host_address[100];
  1552.     boolean  security_flag = false;
  1553.     
  1554.  
  1555.  
  1556.     if ( database_name != NULL ) {
  1557.        sprintf(security_filename,"%s/%s",index_directory,DATASECURITYFILE);
  1558.     }
  1559.     else {
  1560.        sprintf(security_filename,"%s/%s",index_directory,SERVSECURITYFILE);
  1561.     }
  1562.  
  1563.  
  1564.    
  1565.     /* see if we can open the security file - if not then we dont use it */
  1566. /*    if ( probe_file(security_filename,"r") ) { */
  1567.     if ( probe_file(security_filename) ) {
  1568.         waislog(WLOG_HIGH, WLOG_INFO,"Using security file : %s", security_filename);
  1569.     security_stream = s_fopen(security_filename,"r");
  1570.     if ( security_stream  == NULL ) {
  1571.            waislog(WLOG_HIGH, WLOG_ERROR,"Could not open security file : %s", security_filename);
  1572.            return (false);
  1573.     }
  1574.      }
  1575.      else {
  1576.         return (true);
  1577.      }
  1578.        
  1579.     /* loop getting each line from the security file */
  1580.     do {
  1581.       security_line[0] = '\0';
  1582.       security_database_name[0] = '\0';
  1583.       security_host_name[0] = '\0';
  1584.       security_host_address[0] = '\0';
  1585.       
  1586.     /* read the line, skip '#' lines */
  1587.     if ( (fgets(security_line,100,security_stream) != NULL) && (security_line[0] != '#') ) {
  1588.         
  1589.         /* do we have a database name to check against? */
  1590.         if ( database_name != NULL ) {
  1591.             sscanf(security_line,"%s %s %s",security_database_name,security_host_name, security_host_address);
  1592.             
  1593.             /* check the host address if it exists */
  1594.             if ( host_address[0] != 0 && security_host_address[0] != 0 &&
  1595.                 (( strncmp(host_address,security_host_address,strlen(security_host_address)) == 0 &&
  1596.                 strcmp(security_database_name,database_name) == 0) ||  
  1597.                 ( strcmp(security_host_address,"*") == 0 && 
  1598.                 strcmp(security_database_name,database_name) == 0 )) ) {
  1599.                 security_flag = true;
  1600.                 break;
  1601.             }
  1602.         
  1603.             /* check the host name if it exists*/
  1604.             if ( host_name[0] != 0 && security_host_name[0] != 0 &&
  1605.                    (( strncmp(host_name + ( strlen(host_name) - strlen(security_host_name) ),
  1606.                    security_host_name,strlen(security_host_name)) == 0 &&
  1607.                    strcmp(security_database_name,database_name) == 0) ||
  1608.                    ( strcmp(security_host_name,"*") == 0  &&
  1609.                    strcmp(security_database_name,database_name) == 0 )) ) {
  1610.                 security_flag = true;
  1611.                 break;
  1612.             }
  1613.  
  1614.             /* the host name/address could not be resolved, see if we have public access */
  1615.             if ( host_address[0] == 0 && host_name[0] == 0 &&
  1616.                    strcmp(security_host_name,"*") == 0  &&
  1617.                    strcmp(security_host_address,"*") == 0  &&
  1618.                    strcmp(security_database_name,database_name) == 0 ) {
  1619.                 security_flag = true;
  1620.                 break;
  1621.             }
  1622.  
  1623.  
  1624.         }
  1625.         else {
  1626.             sscanf(security_line,"%s %s",security_host_name, security_host_address);
  1627.             
  1628.             /* check the host address */
  1629.             if ( host_address[0] != 0 && security_host_address[0] != 0 &&
  1630.                 ((strncmp(host_address,security_host_address,strlen(security_host_address)) == 0) ||
  1631.                 (strcmp(security_host_address,"*") == 0)) ) {
  1632.                 security_flag = true;
  1633.                 break;
  1634.             }
  1635.         
  1636.             /* check the host name */
  1637.             if ( host_name[0] != 0 && security_host_name[0] != 0 &&
  1638.                    ((strncmp(host_name + ( strlen(host_name) - strlen(security_host_name) ),
  1639.                    security_host_name,strlen(security_host_name)) == 0) ||
  1640. /*                   ((security_host_name,strlen(security_host_name)) == 0) || */
  1641.                    (strcmp(security_host_name,"*") == 0)) ) {
  1642.                 security_flag = true;
  1643.                 break;
  1644.             }
  1645.             
  1646.             /* the host name/address could not be resolved, see if we have public access */
  1647.             if ( host_address[0] == 0 && host_name[0] == 0 &&
  1648.                    strcmp(security_host_name,"*") == 0  &&
  1649.                    strcmp(security_host_address,"*") == 0  ) {
  1650.                 security_flag = true;
  1651.                 break;
  1652.             }
  1653.  
  1654.         }
  1655.             
  1656.      }
  1657.                 
  1658.     } while ( strlen(security_line) != 0 );
  1659.   
  1660.  
  1661.     /* close the security file */
  1662.     s_fclose(security_stream);
  1663.  
  1664.  
  1665.     /* tell the user of an attempted break-in */
  1666.     if ( security_flag == false ) {
  1667.     if ( database_name != NULL ) {
  1668.         if(host_address[0] != 0){
  1669.           waislog(WLOG_MEDIUM, WLOG_ERROR,"Unauthorised access of database '%s' by: %s [%s], %s ",
  1670.           database_name, host_name, host_address, VERSION);
  1671.         }
  1672.         else if(host_name[0] != 0){
  1673.           waislog(WLOG_MEDIUM, WLOG_ERROR,"Unauthorised access of database '%s' by: %s, %s",
  1674.           database_name, host_name,  VERSION);
  1675.         }
  1676.     }
  1677.     else {
  1678.         if(host_address[0] != 0){
  1679.           waislog(WLOG_MEDIUM, WLOG_ERROR,"Unauthorised access by: %s [%s], %s",
  1680.           host_name, host_address, VERSION);
  1681.         }
  1682.         else if(host_name[0] != 0){
  1683.           waislog(WLOG_MEDIUM, WLOG_ERROR,"Unauthorised access by: %s, %s",
  1684.           host_name, VERSION);
  1685.         }
  1686.     }
  1687.     }
  1688.     
  1689.     
  1690.   return(security_flag);
  1691.  
  1692. }
  1693.  
  1694.  
  1695.