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