/********************************************************************
* wilkinson
* 3.24VMS
* 1995/11/24 12:00
* gopher_root1:[gopher.g2.vms2_13.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.24VMS 1995/11/24 12:00    wilkinson
* Use err_ret() and new CMDsetErrored() macro instead of err_quit() when
*   "getcommand:readline error" occurs.
*
* 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);
#ifdef VMS_SERVER
    CMDsetErrored(temp, FALSE);
#endif

    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);
#ifndef VMS_SERVER
         err_quit("getcommand: readline error");
#else
         err_ret("getcommand: readline error");
         CMDsetErrored(cmd, TRUE);
         return;
#endif
    }

    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:: ");
#ifdef VMS_SERVER
    if (CMDisErrored(cmd))
       Debugmsg("errored, ");
#endif
    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);
    ;
}