/********************************************************************
 * lindner
 * 3.18
 * 1994/11/28 03:41:31
 * /home/arcwelder/GopherSrc/CVS/gopher+/gopherd/mindexd.c,v
 * Exp
 *
 * Paul Lindner, University of Minnesota CIS.
 *
 * Copyright 1991, 1992 by the Regents of the University of Minnesota
 * see the file "Copyright" in the distribution for conditions of use.
 *********************************************************************
 * MODULE: mindexd.c
 * Routines to implement a fanout search server
 *********************************************************************
 * Revision History:
 * mindexd.c,v
 * Revision 3.18  1994/11/28  03:41:31  lindner
 * Fix for conflicting env declarations
 *
 * Revision 3.17  1994/11/07  19:54:48  lindner
 * Add default timeout value for mindexd
 *
 * Revision 3.16  1994/11/05  03:19:27  lindner
 * Mindex timeouts, mindex gindexd support from Steve Hsieh
 *
 * Revision 3.15  1994/05/05  03:00:37  lindner
 * Fix for mindex file processing..
 *
 * Revision 3.14  1994/03/31  22:44:33  lindner
 * don't mess with the selector string in mindexd
 *
 * Revision 3.13  1994/03/31  21:09:57  lindner
 * Sanity check for mindexd, don't write to dead sockets
 *
 * Revision 3.12  1994/03/08  16:51:59  lindner
 * Add define to not compile in mindexd stuff
 *
 * Revision 3.11  1994/03/08  16:49:41  lindner
 * Add define to not compile in mindexd stuff
 *
 * Revision 3.10  1994/03/08  15:56:08  lindner
 * gcc -Wall fixes
 *
 * Revision 3.9  1993/10/19  21:00:47  lindner
 * Fix for mindex being stashed into the actual search terms...
 *
 * Revision 3.8  1993/09/22  04:33:15  lindner
 * pl7
 *
 * Revision 3.7  1993/09/11  04:40:39  lindner
 * Don't fork for localhost mindex databases
 *
 * Revision 3.6  1993/08/06  14:36:14  lindner
 * Fix for mindexd and gplus...
 *
 * Revision 3.5  1993/07/29  21:21:27  lindner
 * Removed excess variables
 *
 * Revision 3.4  1993/07/27  05:27:53  lindner
 * Mondo Debug overhaul from Mitra
 *
 * Revision 3.3  1993/07/20  23:52:43  lindner
 * Add .mindex if we can't open the file
 *
 * Revision 3.2  1993/07/13  03:58:44  lindner
 * fixed bug closing an fclose
 *
 * Revision 3.1.1.1  1993/02/11  18:02:56  lindner
 * Gopher+1.2beta release
 *
 * Revision 1.1  1992/12/29  23:27:21  lindner
 * Initial revision
 *
 *
 *********************************************************************/

#ifndef NO_MINDEXD

#include "gopherd.h"
#include "Debug.h"
#include "command.h"
#include <setjmp.h>

#define MAXSLAVES 64    

#ifndef MINDEXTIMEOUT
#  define MINDEXTIMEOUT 180
#endif

struct one_slave {
   int  port;
   char host[128];
   char pathname[512];
   int  sockfd;
   int  pid;
};

struct one_slave slaves[MAXSLAVES];

int LastSlave;
static jmp_buf env;

/* this function is called if it takes over MINDEXTIMEOUT seconds to
   establish a connection */
SIGRETTYPE server_timeout(sig)
     int sig;
{
  longjmp(env,1);
}


/*  read the config file and fill the slaves struct, returns
    the number of slave servers */
int
getconfig( the_filename )
  char *the_filename;
{
    char *cp;
    char *local;
    int i;
    FILE *fp;
    char    line[1024];      /* line buffer for reading config */
    char    *linep;         /* pointer to 'line' */

    strcpy(line, the_filename);
    local = line;

    if ((fp = rfopen(local, "r")) == NULL) {
	 /** Try adding .mindex to the filename **/
	 local = (char *) malloc(sizeof(char*) *(strlen(local) + 10));
	 strcpy(local, the_filename);
	 strcat(local, ".mindex");
	 if ((fp = rfopen(local, "r")) == NULL) {
	      exit(1);
	 }
    }

    LastSlave = -1;
    for (;;) {
        if (fgets(line, sizeof line, fp) == NULL)
               break;  /* done */

        if ((i = strlen(line)))
               line[i-1] = '\0';  /* remove trailing newline */
        if (line[0] == '#' || line[0] == '\0')
               continue;       /* skip comment lines */
        else {
	     char *pathpart;

               LastSlave++;
               linep = line;
               /* the port number is seperated from the host name by a space */
	       /* The path is everything after the port */
	     
 	       pathpart = strchr(strchr(linep, ' ')+1, ' ');
	       if (pathpart != NULL)
		 {
		   *pathpart = '\0';
		   pathpart++;
		   cp = strchr(pathpart, ' ');
		   if (cp != NULL)
		     {
		       /* strip off trailing spaces in pathname*/
		       *cp = '\0';
		     }
		 }

               cp = strchr(linep, ' ');
               slaves[LastSlave].port = atoi(cp+1);
               *cp = '\0'; /* trim off stuff after the hostname */
               cp = slaves[LastSlave].host;
               strcpy(cp, linep);
	       cp = slaves[LastSlave].pathname;
	       if (pathpart != NULL) 
		    strcpy(cp, pathpart);
	       else
		    cp = "";
        }
    }
    fclose(fp);

    return( LastSlave );
}

/*
 * Fork off a process
 */


/* take the query the client passed us, and send it on to the slave index
   servers, gather up their responses and return them to our client 
*/
void
HandleQuery(sockfd, queryline )
  int  sockfd;
  char *queryline;
{
     char      answerline[512];
     int       length_answer;
     int       i;
     int       childpid;
     GopherObj *gs;
     CMDobj    *cmd;
     
     gs = GSnew();
     cmd = CMDnew();
     
     Debug("Number slaves is %d",LastSlave);
     Debug(", query is %s\n", queryline);
     
     /* send queries to all the slaves and gather up responses */
     for (i = 0; i < ( LastSlave + 1); i++ ) {
	  
	  GSsetType(gs, '7');
	  GSsetTitle(gs,"");
	  GSsetHost(gs, slaves[i].host);
	  GSsetPort(gs, slaves[i].port);
	  GSsetPath(gs, slaves[i].pathname);
	  
	  if (strcmp(slaves[i].host, "localhost") == 0 ||
	      strcasecmp(slaves[i].host, Zehostname)  == 0) {
	       CMDsetSelstr(cmd, GSgetPath(gs));
	       CMDsetSearch(cmd, queryline);
	       CMDsetGplus(cmd, FALSE);
	       
	       Do_IndexTrans(sockfd, slaves[i].pathname+1, cmd, FALSE);
	  } else {

	       if ( (childpid = fork()) < 0)
		    err_dump("server: fork error");
	       
	       else if (childpid == 0) {     /* Child process */
		    /* fork child processes to handle each of the slaves */
		    
                    /* time how long a slave has been running,
                       and abort if hasn't finished within 
                       MINDEXTIMEOUT secs */
                    (void) signal(SIGALRM, server_timeout);
                    (void) alarm(MINDEXTIMEOUT);

                    if (setjmp(env))
                      {
                        LOGGopher(sockfd, "during mindex search, unable to establish a connection with %s", slaves[i].host);
                        sprintf(answerline, "0\t\t\t\n0>>> NOTE: unable to connect with %s\t\t\t\n0\t\t\t\n", slaves[i].host);
                        writestring(sockfd, answerline);
                        close(slaves[i].sockfd);
                        exit(-1);
                      }

		    slaves[i].sockfd = GSconnect(gs);
		    
		    (void) alarm(0);

		    if (slaves[i].sockfd , 0) {
			 exit(-1); /* Should do this more nicely */
		    }

		    if (slaves[i].pathname != "")
		      {
			/* a pathname was specified */
			writestring(slaves[i].sockfd, slaves[i].pathname);
			writestring(slaves[i].sockfd, "\t");
			writestring(slaves[i].sockfd, queryline );
			writestring(slaves[i].sockfd, "\r\n");
		      }
		    else
		      {
			/* no pathname; must be trying to connect
			   to an old-style gopher server that still uses
			   gindexd */
			writestring(slaves[i].sockfd, queryline);
			writestring(slaves[i].sockfd, "\r\n");
		      }

		    for(;;) {
			 length_answer = readline(slaves[i].sockfd, answerline, 512);
                         if (length_answer > 0)
                           {
                             if (answerline[0] == '.')
                               {
                                 close(slaves[i].sockfd);
                                 exit(0);
                               }
                             else {
                               writestring(sockfd, answerline);
                             }
                           }
                         else
                           {
			     LOGGopher(sockfd, "during mindex search, lost connection with %s", slaves[i].host);
			     sprintf(answerline, "0\t\t\t\n0>>> NOTE: lost connection with %s during search\t\t\t\n0\t\t\t\n", slaves[i].host);
			     writestring(sockfd, answerline);
                             close(slaves[i].sockfd);
                             exit(-1);
                           }
		    }
	       } else {  /** Parent process **/
		    slaves[i].pid = childpid;
	       }
	       
	       
	  }
     }   
	  
     /* make sure all the children are done */
     while (wait((int * ) 0) != -1) 
	  ;
     
     /* all done now, tell the client we are finished */
     writestring(sockfd, ".\r\n");
	  
}



int
do_mindexd(sockfd, config_filename, search, isgplus, view)
  int sockfd;
  char *config_filename;
  char *search;
  int  isgplus;
  char *view;
{

     if (isgplus) 
	  GSsendHeader(sockfd, -1);

     if ((LastSlave = getconfig(config_filename)) == -1) 
         err_dump("can't read config file" );
     
     HandleQuery(sockfd, search);

     LOGGopher(sockfd, "mindex search using %s.mindex for %s", config_filename, search);

     close(sockfd); 
     return(0);
}

#else
int
do_mindexd(sockfd, config_filename, search, isgplus, view)
  int sockfd;
  char *config_filename;
  char *search;
  int  isgplus;
  char *view;
{
     Abortoutput(sockfd, "Sorry, no mindexd support compiled in this server");
}

#endif
