/***************************************
  $Revision: 1.18 $

  Protocol whois module (pw).  Whois protocol.

  Status: NOT REVUED, NOT TESTED

  ******************/ /******************
  Filename            : protocol_whois.c
  Author              : ottrey@ripe.net
  OSs Tested          : Solaris
  ******************/ /******************
  Copyright (c) 1999                              RIPE NCC
 
  All Rights Reserved
  
  Permission to use, copy, modify, and distribute this software and its
  documentation for any purpose and without fee is hereby granted,
  provided that the above copyright notice appear in all copies and that
  both that copyright notice and this permission notice appear in
  supporting documentation, and that the name of the author not be
  used in advertising or publicity pertaining to distribution of the
  software without specific, written prior permission.
  
  THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS; IN NO EVENT SHALL
  AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY
  DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  ***************************************/
#include <stdio.h>
#include <glib.h>

#include "NAME"

#include "defs.h"
#include "protocol_whois.h"
#include "mysql_driver.h"
#include "query_command.h"
#include "query_instructions.h"
#include "constants.h"
/*
#include "objects.h"
*/
#include "access_control.h"
#include "socket.h"
#include "stubs.h"

void print_hello_banner(Query_environ *qe) { 
  char *str1=NULL;

  SK_cd_puts(&(qe->condat), CVS_NAME);
  SK_cd_puts(&(qe->condat), "% Rights restricted by copyright. See http://www.ripe.net/db/dbcopyright.html\n");
  /* Send the environment aswell. */
  SK_cd_puts(&(qe->condat), "% Environment={");
  str1 = QC_environ_to_string(*qe);
  SK_cd_puts(&(qe->condat), str1);
  free(str1);
  SK_cd_puts(&(qe->condat), "}\n\n");
}


/* process the whois query */
PW_qtype_t process_query(char *input, 
                         Query_environ *qe,
                         Query_command **qc_store)
{
  Query_command *qc=NULL;
    PW_qtype_t query_type;

  if ( strlen(input) == 0) {
    /* An empty query (Ie return) was sent */
    query_type = PW_EMPTY;
  } 
  else {        /* else <==> input_length > 0 ) */
    /* parse query */
    printf("whois got %s\n", input);

    /* Make a new query */    
    qc = QC_new(input, qe);
    *qc_store = qc;
    /* Update the query environment */
    // qe = QC_environ_update(qc, qe);
    
    /* Only do a query if there are keys. */
    if (qc->keys == NULL) {
      query_type = PW_NOKEY;
    }
    else {
      if( strlen(qc->keys) == 0 ) {
        query_type = PW_TEMPLATE;
      }
      else if ( strcmp(qc->keys, "help") == 0 ) {
        query_type = PW_HELP;
      }
      else {
        /* Some real query */
        query_type = PW_REAL;
      }
    }
  }
  return query_type;
}


/* PW_interact() */
/*++++++++++++++++++++++++++++++++++++++
  Interact with the client.

  int sock Socket that client is connected to.

  More:
  +html+ <PRE>
  Authors:
        ottrey

  +html+ </PRE><DL COMPACT>
  +html+ <DT>Online References:
  +html+ <DD><UL>
  +html+ </UL></DL>

  ++++++++++++++++++++++++++++++++++++++*/
void PW_interact(int sock) {
  char input[MAX_INPUT_SIZE];

  int read_result;
  
  char *hostaddress=NULL;
  acl_st acl_rip,   acl_eip;
  acc_st acc_conn,  acc_run, acc_credit;
  PW_qtype_t query_type;
  int will_deny=0;

  
  ip_addr_t acc_ip=IP_ADDR_UNSPEC; /* ip we run accounting for */
  
  Query_environ *qe=NULL;
  Query_instructions *qis=NULL;
  Query_command *qc=NULL;
  
  GList *qitem;
  
  /* Get the IP of the client */
  hostaddress = SK_getpeername(sock);
  printf("SK address: %s\n", hostaddress);
  
  /* Initialize the query environment. */
  qe = QC_environ_new(hostaddress, sock);
  
  /* init to zeros */
  memset( &(qe->condat), 0, sizeof(sk_conn_st));

  /* set the connection data: both rIP and eIP to real IP */
  qe->condat.sock = sock;
  qe->condat.ip = hostaddress;
  SK_getpeerip(sock, &(qe->condat.rIP));
  memcpy( &(qe->condat.eIP), &(qe->condat.rIP), sizeof(ip_addr_t));

  /* check the acl using the realIP, get a copy applicable to this IP */
  AC_check_acl( &(qe->condat.rIP), NULL, NULL, NULL, &acl_rip);
  if( acl_rip.deny ) {
    will_deny=1;
  }
  
  /* XXX log new connection here ?*/

  do {
    /* Read input */
    read_result = SK_cd_gets(&(qe->condat), input, MAX_INPUT_SIZE);
    
    /* read_result < 0 is an error and connection should be closed */
    if (read_result < 0 ) {
      /* log the fact, rtc was set */
    }
    
    query_type = process_query(input, qe, &qc);

    /* check -V option */
    if( ! STRUCT_EQUAL(qe->pIP,IP_ADDR_UNSPEC) ) {     /* has pIP in it */
      qe->condat.eIP = qe->pIP;                   /* set eIP to this IP */
    }
    else {
      qe->condat.eIP = qe->condat.rIP;               /* set eIP to rIP */
    }
    
    /* if we have acc already for any ip */
    if( ! STRUCT_EQUAL(acc_ip, IP_ADDR_UNSPEC) ) {
      /* but it's the current one */
      if( memcmp (&acc_ip, &(qe->condat.eIP), sizeof(ip_addr_t))
          == CMP_EQUAL ) {
        /* in principle, can do nothing. 
           If accounting tree handling is fast enough,
           can commit changes and get new copy */
      }
      else { /* if it is different from current eIP (support for -k -V) */
        /* commit changes */
        AC_commit(&acc_ip, &acc_conn);
        acc_ip = IP_ADDR_UNSPEC;
      }
    }
    
    /* get new acc structs for the eIP unless we already have */
    if( memcmp (&acc_ip, &(qe->condat.eIP), sizeof(ip_addr_t))
        != CMP_EQUAL ) {
      acc_ip = qe->condat.eIP;
      memset( &acc_conn, 0, sizeof(acc_st));
      memset( &acc_run,  0, sizeof(acc_st));
      
      AC_fetch_acc( &(qe->condat.eIP), &acc_run, 100 );
        /* if != AC_OK then have to use the zeroed copy */
    }
     
/* start setting counters in the connection acc from here on 
   decrement the credit counter (needed to prevent QI_execute from
   returning too many results */
    
    /* check ACL (will set denial if bonus limit hit). Calculate credit */
    AC_check_acl( &(qe->condat.eIP), &acc_run, &acc_conn, 
                   &acc_credit, &acl_eip);
    
    if( acl_eip.deny ) {
      acc_conn.denials++;
      will_deny = 1;
    }
    
    if( qe->condat.rtc == 0 ) {
      print_hello_banner(qe);

      if( will_deny ) {
        SK_cd_puts(&(qe->condat), "% Sorry, access denied\n");
      }
      else {

        switch( query_type ) {
        case PW_EMPTY:
        case PW_NOKEY:
          /* The user didn't specify a key, so
             - print moron banner
             - force disconnection of the user. */
          SK_cd_puts(&(qe->condat), "% No search key specified\n");
          qe->condat.rtc = SK_NOTEXT;
          break;
        case PW_HELP:
          SK_cd_puts(&(qe->condat), "% Nothing can help you anymore...:-)\n");
          break;
        case PW_TEMPLATE:
          if (qc->q != 0) {
            SK_cd_puts(&(qe->condat), DF_get_server_query(qc->q)); 
          }
          if (qc->t != 0) {
            SK_cd_puts(&(qe->condat), DF_get_class_template(qc->t)); 
          }
          if (qc->v != 0) {
            SK_cd_puts(&(qe->condat), DF_get_class_template_v(qc->v)); 
          }
          break;
        case PW_REAL:
          qis = QI_new(qc,qe);
          for( qitem = g_list_first(qe->sources_list);
               qitem != NULL;
               qitem = g_list_next(qitem)) {
            /* save the original credit, later check how much was used */
            acc_st copy_credit = acc_credit;

            /* stop as soon as further action considered meaningless */
            if( qe->condat.rtc != 0 ) {
              break;
            }
            
            /* QI will decrement the credit counters */
            QI_execute(qitem->data, qis, qe, &acc_credit );

            /* calc. the credit used, result  into copy_credit */
            AC_acc_addup(&copy_credit, &acc_credit, ACC_MINUS);
            
            /*  increment the person/role counters in the conn_acc by 
                the credit used */
            AC_acc_addup(&acc_conn, &copy_credit, ACC_PLUS);
            acc_conn.queries++;
          }
          QI_free(qis);
          break;
        default: die;
        }

        /* log the connection/query/#results/time to file */ 

        /* now add up to the connection acc */
      }

      QC_free(qc);
      
    } /* if still considered connected */

  } /* do */
  while( qe->k && qe->condat.rtc == 0 && CO_get_whois_suspended() == 0);

  /* Free the hostaddress */
  free(hostaddress);

  SK_cd_close(&(qe->condat));
  
  /* Free the query_environ */
  QC_environ_free(qe);
  
  acc_conn.connections++;
  acc_conn.private_bonus += acc_conn.private_objects;
  AC_commit(&acc_ip, &acc_conn);

} /* PW_interact() */
