/********************************************************************
 * lindner
 * 3.24
 * 1995/01/04 17:42:59
 * /home/arcwelder/GopherSrc/CVS/gopher+/gopherd/command.c,v
 * Exp
 *
 * Paul Lindner, University of Minnesota CIS.
 *
 * Copyright 1991, 1992, 1993 by the Regents of the University of Minnesota
 * see the file "Copyright" in the distribution for conditions of use.
 *********************************************************************
 * MODULE: command.c
 * Routines to parse commands from the client.
 *********************************************************************
 * Revision History:
 * command.c,v
 * Revision 3.24  1995/01/04  17:42:59  lindner
 * small renaming
 *
 * Revision 3.23  1994/12/10  08:24:56  lindner
 * Start building HTTP parser
 *
 * Revision 3.22  1994/12/03  02:14:44  lindner
 * Fix predone searches
 *
 * Revision 3.21  1994/12/02  19:51:14  lindner
 * Weird p removed and fix for prespecified searches
 *
 * Revision 3.20  1994/11/24  08:07:54  lindner
 * Fix for command routines with authentication
 *
 * Revision 3.19  1994/10/19  03:31:59  lindner
 * Another NO_AUTH code hack...
 *
 * Revision 3.18  1994/07/31  04:38:20  lindner
 * Add optional pre-specified search string..
 *
 * Revision 3.17  1994/06/29  05:29:19  lindner
 * Remove Admit1, put in cool stuff
 *
 * Revision 3.16  1994/04/25  20:49:03  lindner
 * Fix for debug code
 *
 * Revision 3.15  1994/04/08  21:08:34  lindner
 * Gcc -Wall fix
 *
 * Revision 3.14  1994/03/30  21:36:34  lindner
 * Fix for binary ask data from Don Gilbert
 *
 * Revision 3.13  1994/03/15  17:57:41  lindner
 * Fix for SCO compiler
 *
 * Revision 3.12  1994/03/08  15:55:25  lindner
 * gcc -Wall fixes
 *
 * Revision 3.11  1994/03/04  23:26:04  lindner
 * Fix for changes in strstring.h
 *
 * Revision 3.10  1994/01/20  06:37:03  lindner
 * Addition for 'h' prefix for selector string
 *
 * Revision 3.9  1993/11/02  05:53:40  lindner
 * more docs
 *
 * Revision 3.8  1993/10/04  06:49:20  lindner
 * ASK data stored in core, for a while..
 *
 * Revision 3.7  1993/09/30  16:56:53  lindner
 * Fix for WAIS and $ requests
 *
 * Revision 3.6  1993/07/29  20:13:32  lindner
 * Removed dead variables
 *
 * Revision 3.5  1993/07/27  05:27:40  lindner
 * Mondo Debug overhaul from Mitra
 *
 * Revision 3.4  1993/07/23  03:10:53  lindner
 * Added CMDgetFile() fcn
 *
 * Revision 3.3  1993/04/09  16:50:24  lindner
 * nothing
 *
 * Revision 3.2  1993/03/24  20:19:40  lindner
 * Fixed memory leak
 *
 * Revision 3.1  1993/03/19  19:56:53  lindner
 * New CMD object
 *
 *
 *********************************************************************/



#include "Malloc.h"
#include "String.h"
#include <stdio.h>
#include "util.h"

#include "command.h"
#include "openers.h"
#include "Debug.h"

CMDobj *
CMDnew()
{
     CMDobj *temp;

     temp = (CMDobj *) malloc(sizeof(CMDobj));

     temp->selstr  = temp->command = temp->search = NULL;

     temp->datafromnet   = STRnew();
     temp->asklines      = STAnew(5);

     temp->view          = STRnew();
     temp->secureuser    = STRnew();
     temp->ticket        = STRnew();

     CMDsetGplus(temp, FALSE);

     return(temp);
}


void
CMDdestroy(cmd)
  CMDobj *cmd;
{
     STRdestroy(cmd->datafromnet);
     STAdestroy(cmd->asklines);
     STRdestroy(cmd->view);
     STRdestroy(cmd->ticket);
     STRdestroy(cmd->secureuser);

     free(cmd);
}

char*
CMDparseHTTP(cmd,cp) 
  CMDobj *cmd;
  char   *cp;
{
     int len;
     if (cp == NULL)
	  return(NULL);

     len = strlen(cp);

     if ((len > 5) && ((strncmp(cp, "GET /",5) == 0))) {
	  ;

	  
	  cp += 5;
	  Debug("HTML item is %s\n", cp + len -4);
	  if ((len >14) && (strcmp(cp + len - 14, " HTTP/1.0")==0))
	       *(cp + len - 14) = '\0';
     }
     return(cp);
}


void
CMDfromNet(cmd, sockfd)
  CMDobj *cmd;
  int    sockfd;
{
     char *cp, *selstr;
     char inputline[512];
     char *field1=NULL, *field2=NULL, *field3 = NULL;
     char *extradata = NULL;
     int length;
     
     length = readline(sockfd, inputline, sizeof(inputline));

     /** Set the alarm signal for about an hour, just in case.. **/
     (void) alarm(60 * 60);


     if (length <= 0) {
	  close(sockfd);
	  err_quit("getcommand: readline error");
     }

     ZapCRLF(inputline);

     Debug("Received: %s\n", inputline);

     CMDsetData(cmd, inputline);

     cp = CMDgetData(cmd);

     cp = CMDparseHTTP(cmd, cp);
     cp = CMDticketfromLine(cmd, cp);

     CMDsetSelstr(cmd, cp);
     selstr = cp;

     /** Find the first field, if it exists... **/
     cp = strchr(selstr, '\t');
     if (cp != NULL) {
	  *cp = '\0';
	  cp++;
	  
	  field1 = cp;
	  
	  /** find the second field, if it exists **/
	  cp = strchr(cp, '\t');
	  if (cp != NULL) {
	       *cp = '\0';
	       cp++;
	       field2 = cp;
	  } else {
	       /** find the third field, if it exists **/
	       if (cp != NULL) 
		    cp = strchr(cp, '\t');
	       if (cp != NULL) {
		    *cp = '\0';
		    cp++;
		    field3 = cp;
	       }
	  }

     }
     /** Okay, now decide which field is the search and 
       which is the command */
     
     if (*selstr == '7' || strncmp(selstr, "waissrc:",8)==0 ||
	 strncmp(selstr, "mindex:",7) ==0) {
	  char *questionmark;
	  /** If it's a type 7 search, search for a ? mark in the
    	   ** selector string.  (Yeah, yeah, it's weblike)
	   **/

	  questionmark = strrchr(selstr, '?');
	  if (questionmark != NULL) {
	       *questionmark = '\0';
	       CMDsetSearch(cmd, questionmark+1);
	       CMDsetCommand(cmd, field1);
	  } else {
	       CMDsetSearch(cmd, field1);
	       CMDsetCommand(cmd, field2);
	  }
     } else {
	  CMDsetCommand(cmd, field1);
	  CMDsetSearch(cmd, NULL);
     }

     /** Get the extra data (from an ask block etc.) if we've got
          an extra data flag... **/

     if (field3 != NULL)
	  extradata = field3;
     else if	 (CMDgetSearch(cmd) == NULL && field2 != NULL)
	  extradata = field2;

     if (extradata != NULL) {
	  CMDgetXtra(cmd, sockfd, atoi(extradata));
     }

     /** Test to see if we've got Gplus behaviour */

     if (CMDgetCommand(cmd) != NULL && *CMDgetCommand(cmd) != '\0')
	  CMDsetGplus(cmd, TRUE);

     Debugmsg("Command:: ");
     Debug("selstr %s, ", CMDgetSelstr(cmd));
     Debug("command %s, ", CMDgetCommand(cmd));
     Debug("search %s, ", CMDgetSearch(cmd));
     Debug("user %s, ", CMDgetUser(cmd));
     Debug("ticket %s, ", CMDgetTicket(cmd));
}


void
CMDpushAskline(cmd, line)
  CMDobj *cmd;
  char   *line;
{
     String *temp = STRnew();

     STRset(temp, line);
     STApush(cmd->asklines, temp);

     STRdestroy(temp);
}

/* dgg need for binary ask data */
void
CMDpushAskBinary(cmd, buf, buflen)
  CMDobj *cmd;
  char   *buf;
  long   buflen;
{
     String *temp = STRnew();
     temp->data= buf;
     temp->len= buflen;
     STApush(cmd->asklines, temp);
     temp->data= NULL; /* caller owns buf */
     STRdestroy(temp);
}


/*
 * Retrieve extra data from the client request..  This stuff is optional
 *
 */

void
CMDgetXtra(cmd, fd, extradata)
  CMDobj *cmd;
  int fd;
  int extradata;
{
     char inputline[512];

     /** Siphon off data if it's there.. **/

     /** A ticket? **/
     if ((extradata & 0x2) == 0x2) {
	  ;
     }

     /** An ask block **/
     if ((extradata & 0x1) == 0x1) {

	  /** Okay, the next line is either +-1, or +bytes .. **/
	  readline(fd, inputline, sizeof(inputline));
	  Debug("received: %s\n", inputline);
	  ZapCRLF(inputline);
	  if (strncmp(inputline, "+-1",3)==0) {
	       while (readline(fd, inputline, sizeof(inputline))>0)  {
		    ZapCRLF(inputline);
		    ZapCRLF(inputline);
		    if (*inputline == '.' && *(inputline+1) == '\0')
			 break;

		    CMDpushAskline(cmd, inputline);
	       }
	  }
	  /* dgg: patch for askfile +bytecount or +-2 input forms */
	  /* dgg++ -- need this to read binary file from client */
	  else { 
	       /*inputline == "+-2" or  "+bytes..." */
	       long count, nbytes;

	       nbytes= atol(inputline+1);
	       if (nbytes == -2) do { /* read til close */
		    count= readrecvbuf(fd,inputline,sizeof(inputline));
		    if (count>0) CMDpushAskBinary(cmd, inputline, count);
	       } while (count>0);
	       else while (nbytes>0) { /* read til nbytes found */
		    count= sizeof(inputline);
		    if (count>nbytes) count= nbytes;
		    count= readrecvbuf(fd,inputline,count);
		    if (count>0) CMDpushAskBinary(cmd, inputline, count);
		    nbytes -= count;
	       }
          }
     }
}



char *
CMDticketfromLine(cmd, input)
  CMDobj *cmd;
  char   *input;
{
#ifndef NO_AUTHENTICATION
     char *cp;
     char *originput = input;
     
     if (*input != '*')
	  return(originput);

     input++;
     cp = strchr(input, ' ');
     if (cp == NULL)
	  return(originput);

     *cp = '\0';
     
     CMDsetUser(cmd,input);

     input = cp+1;
     cp = strchr(input, ' ');
     if (cp == NULL)
	  return(originput);

     *cp = '\0';
     CMDsetTicket(cmd, input);

     return(cp+1);
#endif
     ;
}

/** Find an associated file from the selector string... Yuck! **/

char *
CMDgetFile(cmd)
  CMDobj *cmd;
{
     char *selstr = CMDgetSelstr(cmd);
     char *cp = NULL;

     if (strncmp(selstr, "validate ", 9) == 0)
	  selstr += 9;


     switch (*selstr) {
     case '0':
     case '1':
     case '7':
     case '9':
     case 'h':
     case 's':
     case 'I':
	  return(selstr+1);

     case 'm':
	  if (strncmp(selstr,"mindex:",7)==0)
	       return(selstr+7);
	  else
	       return(selstr+1);

     case 'R':
	  cp = strchr(selstr, '-');
	  if (cp == NULL)  break;

	  cp++;
	  cp = strchr(cp, '-');
	  if (cp == NULL) break;
	  
	  return(cp+1);


	  /*** Exec: ***/
     case 'e':
	  cp = strrchr(selstr, ':');
	  if (cp!=NULL)
               return(cp+1);
	  else
               return(NULL);
	  break;


	  /** WAIS docid **/
     case 'w':
	  if (strncmp(selstr, "waissrc:",8)==0)
	       return(strchr(selstr, ':')+1);
	  
     default:
	  if (*selstr == '\0') {
	       return("//");
	  } else {
	       return(NULL);
	  }
     }

     return(NULL);
     ;
}
