#include <stdio.h>
#include <stdlib.h> 
#include <string.h> 
#include <glib.h>
#include <iostream.h>

    #include <netdb.h> 
    #include <sys/types.h> 
    #include <netinet/in.h> 
    #include <sys/socket.h> 
    #include <errno.h> 

#include <config.h>
#include <istream.h>
#include "rpsl/object.hh"
#include "util/rusage.hh"
#include "util/debug.hh"
#include "util/trace.hh"
#include "util/Argv.hh"
#include "util/version.hh"
#ifdef IRR_NEEDED
#include "irr/irr.hh"
#include "irr/rawhoisc.hh"
#endif // IRR_NEEDED
#include "rpsl/schema.hh"

Rusage ru;
bool opt_stats                   = false;
bool opt_rusage                  = false;
char *opt_prompt                 = "RtConfig> ";
bool opt_echo                    = false;
#ifdef DEBUG
bool opt_debug_rpsl              = false;
#endif // DEBUG

int start_tracing(char *dst, char *key, char *nextArg) {
   if (nextArg) {
      trace.enable(nextArg);
      return 1; // return 1 to signify nextArg is used by us
   }
   return 0; 
}

int start_debugging(char *dst, char *key, char *nextArg) {
   if (nextArg) {
      Debug(dbg.enable(atoi(nextArg)));
      return 1; // return 1 to signify nextArg is used by us
   }
   return 0;
}

void init_and_set_options (int argc, char **argv, char **envp) {
   ArgvInfo argTable[] = {
     // RAToolSet common arguments
     // key, type, src, dst, help
     {"-T", ARGV_FUNC, (char *) &start_tracing,      (char *) NULL, 
      "Start tracing the next argument"},
     {"-D", ARGV_FUNC, (char *) &start_debugging,    (char *) NULL, 
      "Start debugging the next argument"},
     {"-version", ARGV_FUNC, (char *) &version,      (char *) NULL,
      "Show version"},
#ifdef IRR_NEEDED
     {"-h", ARGV_FUNC, (char *) &IRR::ArgvHost,      (char *) NULL,
      "Host name of the RAWhoisd server"},
     {"-p", ARGV_FUNC, (char *) &IRR::ArgvPort,      (char *) NULL,
      "Port number of the RAWhoisd server"},
     {"-s", ARGV_FUNC, (char *) &IRR::ArgvSources,   (char *) NULL,
      "Order of databases"},
     {"-ignore_errors", ARGV_FUNC, (char *)&IRR::IgnoreErrors, (char *)NULL,
      "Ignore IRR error and warning messages"},
     {"-report_errors", ARGV_FUNC, (char *)&IRR::ReportErrors, (char *)NULL,
      "Print IRR error and warning messages"},
#endif // IRR_NEEDED
     {"-rusage", ARGV_BOOL, (char *) NULL,           (char *) &opt_rusage,
      "On termination print resource usage"},
     {"-stats", ARGV_BOOL, (char *) NULL,            (char *) &opt_stats,
      "On termination print class statistics"},
     {"-prompt", ARGV_STRING,  (char *) NULL,        (char *) &opt_prompt,
      "Prompt"},
     
     {"-echo", ARGV_BOOL, (char *) NULL,           (char *) &opt_echo,
      "Echo each object parsed"},
#ifdef DEBUG
     {"-debug_rpsl", ARGV_BOOL, (char *) NULL,     (char *) &opt_debug_rpsl,
      "Turn on bison debugging. Intended for developers."},
#endif // DEBUG
     {(char *) NULL, ARGV_END, (char *) NULL, (char *) NULL,
      (char *) NULL}
   };
  
#ifdef IRR_NEEDED
   IRR::handleEnvironmentVariables(envp);
#endif // IRR_NEEDED

   if (ParseArgv(&argc, argv, argTable, ARGV_NO_LEFTOVERS) != ARGV_OK) {
      cerr << endl;
      exit(1);
   }

#ifdef IRR_NEEDED
   irr = IRR::newClient();
#endif // IRR_NEEDED

   // have a prompt only if the input is coming from a tty
   if (!isatty(fileno(stdin)) || !isatty(fileno(stdout)))
      opt_prompt = NULL;
}

#define MAXDATASIZE 100 /* max number of bytes we can get at once */
#define PORT 11111

#define UP_AUTH_OK 0    /* Authorisation succeeded */
#define ERROR_UP_MOR 1  /* Got more than one objects from the db, error */
#define ERROR_UP_NSO 2  /* so such object */
#define ERROR_UP_AUF 3  /* auth failed */
#define ERROR_UP_NIY 4  /* not implemented yet */
#define ERROR_UP_ABN 5  /* As-block does not exist */
#define ERROR_UP_HOF 6  /* Hierarchical authorisation failed */
#define ERROR_UP_OVF 7  /* override failed */
#define ERROR_UP_OVS 8  /* override syntax error */
#define ERROR_UP_INT 9  /* internal error */

#define OVR_OK 0 /* override succeded */


#define AU_MAIL_FROM 1
#define AU_CRYPT_PW 2
#define AU_PGP 3
#define AU_NONE 4

struct credentials{
   char * password;
   char * from;
   char * pgp_struct;
};


typedef struct _auth_struct{
  int type;
  char * auth;
  char * mntner_name;
  int index;
  char * pgp_struct;
} auth_struct;

int error = 0; // a global variable to store the errors
char * error_msg = NULL; // a global variable to store the error messages


/* sends the object to the database. char * operation is either 'ADD' or 'DEL' */
void send_object_db(char * arg, char * operation){

        int sockfd, numbytes;  
        char buf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr; /* connector's address information */

        if ((he=gethostbyname("rowan")) == NULL) {  /* get the host info */
            perror("gethostbyname");
            exit(1);
        }

        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(1);
        }

        their_addr.sin_family = AF_INET;      /* host byte order */
        their_addr.sin_port = htons(PORT);    /* short, network byte order */
        their_addr.sin_addr = *((struct in_addr *)he->h_addr);
        bzero(&(their_addr.sin_zero), 8);     /* zero the rest of the struct */


        if (connect(sockfd, (struct sockaddr *)&their_addr, 
                                              sizeof(struct sockaddr)) == -1) {
            perror("connect");
            exit(1);
        }
                if (send(sockfd, operation , strlen(operation), 0) == -1)
                    perror("send");
                if (send(sockfd, "\n\n" , strlen("\n\n"), 0) == -1)
                    perror("send");
                if (send(sockfd, arg , strlen(arg), 0) == -1)
                    perror("send");
                if (send(sockfd, "\n",1,0)  == -1)
                    perror("send");


        while ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) != 0) {
            buf[numbytes] = '\0';
            printf("%s",buf);
            /*perror("recv");
            exit(1);*/
        }

        /*buf[numbytes] = '\0';
        printf("Received: %s",buf);*/

        close(sockfd);
}






/* takes a pre-parsed object, and returns its type */
char * get_type(Object *arg){
    
    char * be_returned = NULL;
    if(arg == NULL) return NULL;
    be_returned = strdup(arg->type->getName());  
    return g_strstrip(be_returned);
}






/* takes an object (pre-parsed) and returns its first attrib if it is not
   a person, and returns the nic-hdl if it is a person object */
char * get_search_key(Object *arg, char * type, char * text){

    
    Attr *attr;    
    char *primary_key = NULL, *value = NULL;

    if(arg == NULL) return NULL;

          for(attr = arg->attrs.head(); attr; attr = arg->attrs.next(attr)){
            value = (char*)malloc((*attr).len - strlen(attr->type->name()) - 1);
            strncpy(value, (char *)(text+attr->offset) + strlen(attr->type->name())+1,
                attr->len - strlen(attr->type->name()) -2 );
            value[attr->len - strlen(attr->type->name()) - 2 ] = '\0';
            //cout << "value: #" << value << "#" << endl;
            if(strcmp(attr->type->name(),type) == 0 &&
               strcmp(type,"person") != 0){
              primary_key = strdup(value);
            }
            if(strcmp(attr->type->name(),"nic-hdl") == 0 &&
               strcmp(type,"person") == 0){
              primary_key = strdup(value);
            }
          }
   
    return g_strstrip(primary_key);
}




/* sends char * arg to the specified host's specified port, and
   returns the reply as a string */
char * send_and_get(char * host, int port, char * arg){

        int sockfd, numbytes; 
        char * result = NULL; 
        char buf[MAXDATASIZE];
        struct hostent *he;
        struct sockaddr_in their_addr; /* connector's address information */

        if ((he=gethostbyname(host)) == NULL) {  /* get the host info */
            perror("gethostbyname");
            exit(1);
        }

        if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
            perror("socket");
            exit(1);
        }

        their_addr.sin_family = AF_INET;      /* host byte order */
        their_addr.sin_port = htons(port);    /* short, network byte order */
        their_addr.sin_addr = *((struct in_addr *)he->h_addr);
        bzero(&(their_addr.sin_zero), 8);     /* zero the rest of the struct */


        if (connect(sockfd, (struct sockaddr *)&their_addr, 
                                              sizeof(struct sockaddr)) == -1) {
            perror("connect");
            exit(1);
        }
                if (send(sockfd, arg , strlen(arg), 0) == -1)
                    perror("send");
                if (send(sockfd, "\n",1,0)  == -1)
                    perror("send");


        while ((numbytes=recv(sockfd, buf, MAXDATASIZE, 0)) != 0) {
            buf[numbytes] = '\0';
            if(result == NULL){
              result = strdup(buf);
            }else{
              result = (char *)realloc(result, strlen(result) + strlen(buf));
              result = strcat(result, buf);
            }
        }

        close(sockfd);
        return result;


}

/* counts the number of objects in a string */
int count_objects(char * arg){
    int count = 0;
    char *pos = NULL;
    char *temp = strdup(arg);
    
    if(isalpha(arg[0])){
      count++;
    }else if(arg[0] == '\n' && isalpha(arg[1])){
      count++;
    }
    while(pos = strstr(temp,"\n\n")){
      pos[0] = 'a'; // something non-EOL so that it won't be caught in the next loop
      if(isalpha(pos[2])){
        count++;
      }
    }
    cout << "DEBUG: count_objects returning " << count << endl;
    return count;
}




/*  */
char * take_object(char * arg){
    char * returned;
    char * object = NULL, * pos = NULL;
    char * temp = strdup(arg);
    
    if(isalpha(temp[0])){
      if(strstr(temp,"\n\n") == NULL){
        return temp;
      }else{
        pos = strstr(temp,"\n\n");
        pos[0] = '\0';
        return temp;
      }
    }else if(temp[0] == '\n' && isalpha(temp[1])){
      if(strstr(temp,"\n\n") == NULL){
        return (char *)temp[1];
      }else{
        pos = strstr(temp,"\n\n");
        pos[0] = '\0';
        return (char *)temp[1];
      }
    }else{
      temp = strstr(temp,"\n\n");
      temp = temp + 2;
      if(strstr(temp,"\n\n") == NULL){
        return temp;
      }else{
        pos = strstr(temp,"\n\n");
        pos[0] = '\0';
        return temp;
      }
    }
}





/* Takes an autnum_object, and returns the as-block containing this aut-num */
char * get_as_block(char *autnum_object){
  bool code;
  char * search_key = NULL, * query_string = NULL;
  char * result = NULL;
  Object * o = new Object();
  
  code = o->scan(autnum_object, strlen(autnum_object));
  search_key = get_search_key(o,"aut-num",autnum_object);
  
  query_string = (char *)malloc(strlen("-Tas-block -r ")+strlen(search_key)+1);
  sprintf(query_string, "-Tas-block -r %s",search_key);
  result = send_and_get("reimp", 43, query_string);
  if(count_objects(result) == 0){
    cout << "No such as-block" << endl;
    return NULL;
  }else if(count_objects(result) > 1){
    cout << "More than one as-block returned" << endl;
    return NULL;
  }else{ // count_objects(result) == 1
    return take_object(result);
  }
  
}





/* Takes a domain_object, and returns the less specific domain of it */
char * get_less_specific_domain(char *domain_object){
  bool code;
  char * search_key = NULL, * query_string = NULL;
  char * result = NULL, * domain = NULL;
  Object * o = new Object();
  int i,j, length;
  char * temp = NULL;
  char ** splitted;

  code = o->scan(domain_object, strlen(domain_object));
  domain = get_search_key(o,"domain",domain_object);

  /* split the domain from its dots ('50' is the max # of pieces, this number is just arbitrary) */
  splitted =   g_strsplit((char *)strdup(domain), ".", 50);

  for(i=1; splitted[i] != NULL; i++){
    /* in the following for loop, we will construct the 'less spec' domains
       to be looked up in the DB */ 
    for(j=i; splitted[j] !=NULL; j++){
      length = 0;
      if(temp!=NULL){
        length = strlen(temp); 
      }
      temp = (char *)realloc(temp, length + strlen(splitted[j]) + 2); 
      if(j==i){
        temp = (char *)strdup(splitted[j]);
      }else{
        sprintf(temp, "%s.%s", temp, splitted[j]);
      }
    }
    query_string = (char *)malloc(strlen("-Tdomain -r -R ")+strlen(temp)+1);
    sprintf(query_string, "-Tdomain -r -R %s", temp);
    result = send_and_get("reimp", 43, query_string);
    if(count_objects(result) == 0){
    }else if(count_objects(result) > 1){
      cout << "DEBUG: get_less_specific_domain: More than one domains returned" << endl;
      return NULL; /* error condition */
    }else{ /* count_objects(result) == 1 */
      return take_object(result);
    }
    
  }
  /* release the memory allocated to **splitted */
  for(i=0; splitted[i] != NULL; i++){ 
    free(splitted[i]);
  }  
  /* so, we couldn't  find any 'less specific' domain */
  return NULL;
}





/* Takes a hierarchical set_object, and returns the less specific set or auth-num of it
   by striping down the object's name ( eg, for as35:rs-trial:rs-myset, 
   as35:rs-trial is tried ) */
char * get_less_specific_set(char *set_object, char *type){
  bool code;
  char * search_key = NULL, * query_string = NULL;
  char * result = NULL;
  Object * o = new Object();
  int i;
  
  code = o->scan(set_object, strlen(set_object));
  search_key = get_search_key(o, type, set_object);
  delete(o);

  for(i = strlen(search_key) -1; i > -1; i--){
    if(search_key[i] == ':'){
      search_key[i] = '\0'; /* truncate the string */
      break;
    }
    if(i == 0){/* if we've reached the beginning of the string 
                (this means there wasn't any ';' in the string) */
      free(search_key);
      search_key = NULL;
    }
  }
  if( search_key == NULL || strlen(search_key) == 0){/* this mustn't happen in fact, since 
                                                        we make sure that the name of the 
                                                        set_object contains a ':' in a proper place */
    return NULL;
  }

   
  query_string = (char *)malloc(strlen("-Taut-num,as-set,rtr-set,peering-set,filter-set -r ")+strlen(search_key)+1);
  sprintf(query_string, "-Taut-num,as-set,rtr-set,peering-set,filter-set -r  %s", search_key);
  result = send_and_get("reimp", 43, query_string);
  if(count_objects(result) == 0){
    cout << "No such object"  << endl;
    return NULL;
  }else if(count_objects(result) > 1){
    cout << "More than one objects returned" << endl;
    return NULL;
  }else{ // count_objects(result) == 1
    return take_object(result);
  }
  
}







/* Takes an inetnum or inet6num object and returnes one less specific of it */
char * get_less_specific(char *inetnum_object, char *type){
  bool code;
  char * search_key = NULL, * query_string = NULL;
  char * result = NULL;
  Object * o = new Object();
  
  code = o->scan(inetnum_object, strlen(inetnum_object));
  search_key = get_search_key(o, type, inetnum_object);
  
  query_string = (char *)malloc(strlen("-Tinet6num -r -l")+strlen(search_key)+1);
  sprintf(query_string, "-T%s -r -l %s",type, search_key);
  result = send_and_get("reimp", 43, query_string);
  if(count_objects(result) == 0){
    cout << "No such " << type << endl;
    return NULL;
  }else if(count_objects(result) > 1){
    cout << "More than one " << type << " returned" << endl;
    return NULL;
  }else{ // count_objects(result) == 1
    return take_object(result);
  }
  
}




/* Gets an object as a string and returns its 'mnt-by' attributes as a 
   GSList (linked list)   */

GSList *get_mntners(char * object){
  bool code;
  Object * o;
  Attr *attr;
  char *value  = NULL;
  GSList *list_of_mntners = NULL;


  cout << "DEBUG: get_mntners is running" << endl;
  o = new Object;
  code = o->scan(object,strlen(object));
  
  for(attr = o->attrs.head(); attr; attr = o->attrs.next(attr)){
    value = (char*)malloc((*attr).len - strlen(attr->type->name()) - 1);
    strncpy(value, (char *)(object+attr->offset) + strlen(attr->type->name())+1,
        attr->len - strlen(attr->type->name()) -2 );
    value[attr->len - strlen(attr->type->name()) - 2 ] = '\0';
    //cout << "value: #" << value << "#" << endl;
    if(strcmp(attr->type->name(),"mnt-by") == 0){
      cout << "DEBUG: get_mntners: adding " << g_strstrip(value) << endl;
      list_of_mntners = g_slist_append(list_of_mntners, strdup(g_strstrip(value)));
    }
  }


  return list_of_mntners; 
}





/* Gets a (maintainer) object as a string and returns its 'auth' attributes 
   as a GSList (linked list) */

GSList *get_auths(char * object){
  bool code;
  Object * o;
  Attr *attr;
  char *value  = NULL;
  GSList *list_of_auths = NULL;


  cout << "DEBUG: get_auths is running" << endl;
  o = new Object;
  code = o->scan(object,strlen(object));
  
  for(attr = o->attrs.head(); attr; attr = o->attrs.next(attr)){
    value = (char*)malloc((*attr).len - strlen(attr->type->name()) - 1);
    strncpy(value, (char *)(object+attr->offset) + strlen(attr->type->name())+1,
        attr->len - strlen(attr->type->name()) -2 );
    value[attr->len - strlen(attr->type->name()) - 2 ] = '\0';
    //cout << "value: #" << value << "#" << endl;
    if(strcmp(attr->type->name(),"auth") == 0){
      cout << "DEBUG: get_auths: adding " << g_strstrip(value) << endl;
      list_of_auths = g_slist_append(list_of_auths, strdup(g_strstrip(value)));
      cout << "DEBUG: get_auths: # of nodes in list_of_auths is now " << g_slist_length(list_of_auths) << endl;
    }
  }

  cout << "DEBUG: get_auths: returning" << endl;
  return list_of_auths; 
}





/* Gets an object as a string an returns its mnt_lower attributes as a 
   GSList (linked list) */

GSList *get_mnt_lowers(char * object){
  bool code;
  Object * o;
  Attr *attr;
  char *value  = NULL;
  GSList *list_of_mnt_lowers = NULL;


  cout << "DEBUG: get_mnt_lowers is running" << endl;
  o = new Object;
  code = o->scan(object,strlen(object));
  
  for(attr = o->attrs.head(); attr; attr = o->attrs.next(attr)){
    value = (char*)malloc((*attr).len - strlen(attr->type->name()) - 1);
    strncpy(value, (char *)(object+attr->offset) + strlen(attr->type->name())+1,
        attr->len - strlen(attr->type->name()) -2 );
    value[attr->len - strlen(attr->type->name()) - 2 ] = '\0';
    //cout << "value: #" << value << "#" << endl;
    if(strcmp(attr->type->name(),"mnt-lower") == 0){
      cout << "DEBUG: get_mnt_lowers: adding " << g_strstrip(value) << endl;
      list_of_mnt_lowers = g_slist_append(list_of_mnt_lowers, strdup(g_strstrip(value)));
    }
  }


  return list_of_mnt_lowers; 
}





/* retrieves the override password from the 'override' attribute  
   of the object. If none, it returns NULL   */
char *get_override(char * object){
  bool code;
  Object * o;
  Attr *attr;
  char *value  = NULL;


  cout << "DEBUG: get_override is running" << endl;
  o = new Object;
  code = o->scan(object,strlen(object));
  
  for(attr = o->attrs.head(); attr; attr = o->attrs.next(attr)){
    value = (char*)malloc((*attr).len - strlen(attr->type->name()) - 1);
    strncpy(value, (char *)(object+attr->offset) + strlen(attr->type->name())+1,
        attr->len - strlen(attr->type->name()) -2 );
    value[attr->len - strlen(attr->type->name()) - 2 ] = '\0';
    //cout << "value: #" << value << "#" << endl;
    if(strcmp(attr->type->name(),"override") == 0){
      cout << "DEBUG: get_override: returning " << g_strstrip(value) << endl;
      return  strdup(g_strstrip(value));
    }
  }
  /* there was no 'override' attrib, so return NULL */
  return NULL; 
}






/* checks override string (password) 
   returns OVR_OK if it is correct password */
int check_override(char * string){
   return OVR_OK; // for now
}







/* takes a GSList of struct auth_struct and a GSList of auths, and a mntner name,
   add new elements to GSList of struct auth_struct and  returns the new
   GSList of struct auth_struct  */

GSList * add_to_auth_vector(GSList * list_of_auth_struct, GSList * auths, char * mntner_name){
   //GSList * to_be_returned = NULL;
   GSList * next;
   char * auth_attrib = NULL;
   char * auth_attrib_uppercase = NULL, * argument = NULL;
   struct auth_struct * temp = NULL;
   int index = 1;
      
   for(next = auths; next != NULL; next = g_slist_next(next)){
     auth_attrib = strdup((char *)next->data);
     auth_attrib = g_strstrip(auth_attrib);
     cout << "DEBUG: add_to_auth_vector: " << auth_attrib << endl;
     /* Take the auth attribute and convert it into uppercase for comparisons */
     auth_attrib_uppercase = strdup(auth_attrib);
     g_strup(auth_attrib_uppercase);
     
     if(strstr(auth_attrib_uppercase,"CRYPT-PW") == auth_attrib_uppercase){
       /* take the argument of the auth attribute */
       argument = strdup(auth_attrib + strlen("CRYPT-PW"));
       g_strstrip(argument);
       cout << "DEBUG: add_to_auth_vector: adding new argument: " << argument << endl;
       temp = (struct auth_struct *)malloc(sizeof(auth_struct));
       temp->type = AU_CRYPT_PW;
       temp->auth = argument;
       temp->mntner_name = mntner_name;
       temp->index = index++;
       list_of_auth_struct = g_slist_append(list_of_auth_struct, temp);
     }else if(strstr(auth_attrib_uppercase,"MAIL-FROM") == auth_attrib_uppercase){
       /* take the argument of the auth attribute */
       argument = strdup(auth_attrib + strlen("MAIL-FROM"));
       g_strstrip(argument);
       cout << "DEBUG: add_to_auth_vector: adding new argument: " << argument << endl;
       temp = (struct auth_struct *)malloc(sizeof(auth_struct));
       temp->type = AU_MAIL_FROM;
       temp->auth = argument;
       temp->mntner_name = mntner_name;
       temp->index = index++;
       list_of_auth_struct = g_slist_append(list_of_auth_struct, temp);
    }else if(strstr(auth_attrib_uppercase,"PGP-") == auth_attrib_uppercase){
       temp = (struct auth_struct *)malloc(sizeof(auth_struct));
       temp->type = AU_PGP;
       temp->mntner_name = mntner_name;
       temp->index = index++;
       /* temp->pgp_struct must be assigned, not yet implemented */
       cout << "Not implemented totally (PGP)" << endl;
     }else{
       cout << "Error: invalid auth attrib: " << auth_attrib << endl;
       return NULL;
     }
   }
   free(auth_attrib_uppercase);
   free(auth_attrib); 
   return list_of_auth_struct;

}










/* constructs the authorisation vector, which is a GSList of
   struct auth_struct */

GSList * get_auth_vector(GSList * mntners){
  GSList * list_of_auths = NULL;
  GSList * next = NULL;
  GSList * to_be_returned = NULL;
  char * query_string = NULL, * result = NULL, * object = NULL;
  GSList * temp;

  for( next = mntners; next != NULL ; next = g_slist_next(next) ){
    cout << "=====" << endl << "Got a mntner" << endl;
    cout << (char *)next->data << endl;
    query_string = (char *)malloc(strlen("-Tmntner -r ")+strlen((char *)next->data)+1);
    sprintf(query_string, "-Tmntner -r %s",(char *)next->data);
    result = send_and_get("reimp", 43, query_string);
    if(count_objects(result) == 0){
      cout << "No such maintainer, exiting" << endl;
      exit(1);
    }else if(count_objects(result) > 1){
      cout << "More than one objects returned" << endl;
    }else{ /* count_objects(result) == 1 */
      object = take_object(result);
      cout << "DEBUG: get_auth_vector: Calling get_auths(char *)" << endl;
      temp = get_auths(object);
      cout << "DEBUG: get_auth_vector: get_auths(char *) returned" << endl;
      list_of_auths = g_slist_concat(list_of_auths, temp);
      to_be_returned = add_to_auth_vector(to_be_returned, list_of_auths, (char *)next->data);
    }
  }
  
  return to_be_returned; 
}








/* Check authorisation
   Applies authorisation rules according to the object type */

int check_auth(char *new_object, char *old_object, char *type){
   
   GSList *old_mntners = NULL, *new_mntners = NULL;
   GSList *old_auths = NULL, *new_auths = NULL;
   GSList *as_block_mnt_lowers = NULL;
   GSList *old_auth_vector = NULL, *new_auth_vector = NULL, *as_block_auth_vector = NULL;
   GSList *less_specific_auth_vector = NULL, *less_specific_mnt_lowers = NULL;
   GSList *less_specific_mntners = NULL;
   
   char *as_block_object = NULL, *less_specific_object = NULL;
   char *less_specific_domain = NULL;
   char *less_specific_object_type = NULL;
   int overriden = 0;
   char *override_string = NULL;
   char *set_name = NULL;
   Object *set_object = new Object();
   Object *temp_obj;
   bool code;
   
   /* first check if it is overriden or not. if overriden, check the override
      password. If it is correct, continue, setting "overriden" to 1. If not,   
      immediately exit returning ERR_UP_OVF                                   */
   override_string = get_override((new_object == NULL) ? old_object : new_object );
   if(override_string == NULL){ 
     overriden = 0;
   }else if(check_override(override_string) == OVR_OK){
     overriden = 1; // authorisation is overriden
   }else if(check_override(override_string) == ERROR_UP_OVS){
     return ERROR_UP_OVS; // override syntax error --it must have at least two words
   }else{
     return ERROR_UP_OVF; // override failed!
   }


   /*  
    *  Handle the "person", "role", "limerick", "inet-rtr" types 
    */
   if(strcmp(type,"person")   == 0 || strcmp(type,"role")     == 0 ||
      strcmp(type,"limerick") == 0 || strcmp(type,"inet-rtr") == 0 ){
     if( new_object == NULL && old_object != NULL ){ // the object is to be deleted
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       //return authorise(old_auth_vector, overriden, ...);
     }else if( new_object != NULL && old_object == NULL ){ // the object is to be created
       new_mntners = get_mntners(new_object);
       new_auth_vector = get_auth_vector(new_mntners);
       //return authorise(new_auth_vector, overriden, ...);
     }else if( new_object != NULL && old_object != NULL ){ // this is an update
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       if(old_auth_vector){ // if we have mntners in the old object, use them
         //return authorise(old_auths_vector, overriden, ...);
       }else{
         new_mntners = get_mntners(new_object);
         new_auth_vector = get_auth_vector(new_mntners);
         //return authorise(new_auth_vector, overriden, ...);
       }
     }else{ // both are NULL, mustn't happen
         cout << "DEBUG: check_auth: internal error: Both pointers are NULL" << endl;
         return ERROR_UP_INT; /* internal error */
     }
   }

   /*  
    *  Handle the "auth-num" type 
    */
   else if(strcmp(type,"aut-num")  == 0 ){
     if( new_object == NULL && old_object != NULL ){ /* the object is to be deleted */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       //return authorise(old_auth_vector, overriden, ...);
     }else if( new_object != NULL && old_object == NULL ){ /* the object is to be created */
       as_block_object = get_as_block(new_object);
       if(as_block_object == NULL ){
         return ERROR_UP_ABN; /* As-block does not exist */
       }else{
         as_block_mnt_lowers = get_mnt_lowers(as_block_object);
         as_block_auth_vector = get_auth_vector(as_block_mnt_lowers);
         if(1/* authorise(as_block_auth_vector, overriden, ...) == UP_AUTH_OK */){
           new_mntners = get_mntners(new_object);
           new_auth_vector = get_auth_vector(new_mntners);
           //return authorise(new_auth_vector, overriden, ...);
         }else{
           return ERROR_UP_HOF; /* hierarchical auth failed */
         }
       }
     }else if( new_object != NULL && old_object != NULL ){ /* this is an update */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       if(old_auth_vector){ /* if we have mntners in the old object, use them */
         //return authorise(old_auth_vector, overriden, ...);
       }else{
         new_mntners = get_mntners(new_object);
         new_auth_vector = get_auth_vector(new_mntners);
         //return authorise(new_auth_vector, overriden, ...);
       }
     }else{ /* both are NULL, mustn't happen */
         cout << "DEBUG: check_auth: internal error: Both pointers are NULL" << endl;
         return ERROR_UP_INT; /* internal error */
     }
   } 

   /*  
    *  Handle the "mntner/as-block" types 
    */
   else if(strcmp(type,"mntner")  == 0 || strcmp(type,"as-block")  == 0 ){
     if( new_object == NULL && old_object != NULL ){ /* the object is to be deleted */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       //return authorise(old_auth_vector, overriden, ...);
     }else if( new_object != NULL && old_object == NULL ){ /* the object is to be created */
       if(overriden){
         return UP_AUTH_OK; 
       }else{/* If not overriden, and if not coming from ripe-dbm, must be forwarded to ripe-dbm */
         cout << "DEBUG: check_auth: '" << type << "' creation requested" << endl;
       }
     }else if( new_object != NULL && old_object != NULL ){ /* this is an update */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       if(old_auth_vector){ /* if we have mntners in the old object, use them */
         //return authorise(old_auth_vector, overriden, ...);
       }else{
         new_mntners = get_mntners(new_object);
         new_auth_vector = get_auth_vector(new_mntners);
         //return authorise(new_auth_vector, overriden, ...);
       }
     }else{ // both are NULL, mustn't happen
         cout << "DEBUG: check_auth: internal error: Both pointers are NULL" << endl;
         return ERROR_UP_INT; /* internal error */
     }
   }

   /*  
    *  Handle the "inetnum/inet6num" types 
    */
   else if(strcmp(type,"inetnum")  == 0 || strcmp(type,"inet6num")  == 0 ){
     if( new_object == NULL && old_object != NULL ){ /* the object is to be deleted */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       //return authorise(old_auth_vector, overriden, ...);
     }else if( new_object != NULL && old_object == NULL ){ /* the object is to be created */
       less_specific_object = get_less_specific(new_object, type);
       if(less_specific_object == NULL){
         if(overriden){
           return UP_AUTH_OK; 
         }else{
           return ERROR_UP_HOF; /* hierarchical authorisation failed */
         }
       }else{ /* if we got an inet(6)num object */
         less_specific_mnt_lowers = get_mnt_lowers(less_specific_object);
         less_specific_auth_vector = get_auth_vector(less_specific_mnt_lowers);
         if(1/* authorise(less_specific_auth_vector, overriden, ...) == UP_AUTH_OK */){
           new_mntners = get_mntners(new_object);
           new_auth_vector = get_auth_vector(new_mntners);
           //return authorise(new_auth_vector, overriden, ...);
         }else{
           return ERROR_UP_HOF; /* hierarchical authorisation failed */
         }
       }
     }else if( new_object != NULL && old_object != NULL ){ /* this is an update */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       if(old_auth_vector){ // if we have mntners in the old object, use them
         //return authorise(old_auth_vector, overriden, ...);
       }else{
         new_mntners = get_mntners(new_object);
         new_auth_vector = get_auth_vector(new_mntners);
         //return authorise(new_auth_vector, overriden, ...);
       }
     }else{ // both are NULL, mustn't happen
         cout << "DEBUG: check_auth: internal error: Both pointers are NULL" << endl;
         return ERROR_UP_INT; /* internal error */
     }
   }


   
   /*  
    *  Handle the "domain" type 
    */
   else if(strcmp(type,"domain")  == 0){
     if( new_object == NULL && old_object != NULL ){ /* the object is to be deleted */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       //return authorise(old_auth_vector, overriden, ...);
     }else if( new_object != NULL && old_object == NULL ){ /* the object is to be created */
       /* now, we have to find a 'less specific domain object' for this. 
          If there is no less specific object, then creation is possible
          only with overriding. */
      less_specific_domain = get_less_specific_domain(new_object);
      if(less_specific_domain == NULL){
        if(overriden){/* we didn't get a 'less specific' domain object */
           return UP_AUTH_OK; 
         }else{
           return ERROR_UP_HOF; /* hierarchical authorisation failed */
         }
      }else{ /* we get a 'less specific' domain object */
         less_specific_mnt_lowers = get_mnt_lowers(less_specific_domain);
         less_specific_auth_vector = get_auth_vector(less_specific_mnt_lowers);
         if(1/* authorise(less_specific_auth_vector, overriden, ...) == UP_AUTH_OK */){
           new_mntners = get_mntners(new_object);
           new_auth_vector = get_auth_vector(new_mntners);
           //return authorise(new_auth_vector, overriden, ...);
         }else{
           return ERROR_UP_HOF; /* hierarchical authorisation failed */
         }
        
      }
     }else if( new_object != NULL && old_object != NULL ){ /* this is an update */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       if(old_auth_vector){ /* if we have mntners in the old object, use them */
         //return authorise(old_auth_vector, overriden, ...);
       }else{
         new_mntners = get_mntners(new_object);
         new_auth_vector = get_auth_vector(new_mntners);
         //return authorise(new_auth_vector, overriden, ...);
       }
     }else{ // both are NULL, mustn't happen
         cout << "DEBUG: check_auth: internal error: Both pointers are NULL" << endl;
         return ERROR_UP_INT; /* internal error */
     }
   }
   

   /*  
    *  Handle the set objects ("as-set","rtr-set","peering-set" and "filter-set" types 
    */
   else if(strcmp(type,"as-set")       == 0 || strcmp(type,"rtr-set")     == 0 ||
           strcmp(type,"peering-set")  == 0 || strcmp(type,"filter-set")  == 0 ){
     if( new_object == NULL && old_object != NULL ){ /* the object is to be deleted */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       //return authorise(old_auth_vector, overriden, ...);
     }else if( new_object != NULL && old_object == NULL ){ /* the object is to be created */
        code = set_object->scan(new_object, strlen(new_object));
        set_name = get_search_key(set_object, type, new_object);
       if(strstr(set_name,":") == NULL ){/* if the name is _not_ hierarchical */
         new_mntners = get_mntners(new_object);
         new_auth_vector = get_auth_vector(new_mntners);
         //return authorise(new_auth_vector, overriden, ...);
       }else{/* the name is hierarchical */
         less_specific_object = get_less_specific_set(new_object, type);
         if(less_specific_object != NULL){/* such an object exists */
           temp_obj = new Object();
           code = temp_obj->scan(less_specific_object, strlen(less_specific_object));
           less_specific_object_type = get_type(temp_obj);
           delete(temp_obj);
           if(strcmp(less_specific_object_type, "aut-num") == 0){/* if this is an aut-num object */
             less_specific_mnt_lowers = get_mnt_lowers(less_specific_object);
             less_specific_auth_vector = get_auth_vector(less_specific_mnt_lowers);
             if(less_specific_auth_vector != NULL){
               //return authorise((less_specific_auth_vector, overriden, ...);
             }else{/* the less specific object doesn't contain any mnt-lower */
               less_specific_mntners = get_mntners(less_specific_object);
               less_specific_auth_vector = get_auth_vector(less_specific_mntners);
               if(less_specific_auth_vector != NULL){/* less spec object has some mnt-by attribs, 
                                                        use them  */
                   //return authorise((less_specific_auth_vector, overriden, ...);
               }else{/* the less specific object doesn't contain any mnt-by either */
                 if(overriden){
                   return UP_AUTH_OK; 
                 }else{
                   return ERROR_UP_HOF; /* hierarchical authorisation failed */
                 }
               }
             }
           }else{ /* this is _not_ an aut-num object*/
             less_specific_mntners = get_mntners(less_specific_object);
             less_specific_auth_vector = get_auth_vector(less_specific_mntners);
             if(less_specific_auth_vector != NULL ){/* the set obj has some mnt-by attribs */
               //return authorise((less_specific_auth_vector, overriden, ...);
             }else{
               if(overriden){
                 return UP_AUTH_OK; 
               }else{
                 return ERROR_UP_HOF; /* hierarchical authorisation failed */
               }
             }
           }

         }else{/* we don't have a less specific of this set object in the DB  */
           return ERROR_UP_HOF; /* hierarchical authorisation failed */
         }
       }
     }else if( new_object != NULL && old_object != NULL ){ /* this is an update */
       old_mntners = get_mntners(old_object);
       old_auth_vector = get_auth_vector(old_mntners);
       if(old_auth_vector){ /* if we have mntners in the old object, use them */
         //return authorise(old_auth_vector, overriden, ...);
       }else{
         new_mntners = get_mntners(new_object);
         new_auth_vector = get_auth_vector(new_mntners);
         //return authorise(new_auth_vector, overriden, ...);
       }
     }else{ /* both are NULL, mustn't happen */
         cout << "DEBUG: check_auth: internal error: Both pointers are NULL" << endl;
         return ERROR_UP_INT; /* internal error */
     }
   




   }else{ /* other types are not implemented yet */
     cout << "check_auth: This type '" << type << "' is not implemented yet" << endl;
     return ERROR_UP_NIY; /* not implemented yet */
   }
   return UP_AUTH_OK; // for now
}






/* Gets the old version of the given "arg" object, which is in char * format
   and returns the old version again in char * format */

char * get_old_version(char * arg){

    bool code = true;
    char *type=NULL, *primary_search_key = NULL, *search_string = NULL;
    Object *o;
    o = new Object;
    char *result = NULL;
    
     
    code = o->scan(arg,strlen(arg));
    type = get_type(o);
    primary_search_key = get_search_key(o,type, arg);
    cout << "type=" << type << endl;
    cout << "primary_search_key=" << primary_search_key << endl;
    // prepare the search string
    search_string = (char *)malloc(strlen(primary_search_key) + strlen("-x -R -r -T")
                                          + strlen(type) + 1);
    sprintf(search_string, "-x -R -r -T%s %s",type, primary_search_key);
    result = send_and_get("reimp", 43, search_string);
    cout << "send_and_get returned (with search '"<< search_string <<"'): " << endl 
         << result << endl;
    // count the objects
    if(count_objects(result) == 0){
      result = NULL; // we don't have any object
    }else if(count_objects(result) == 1){
      result = take_object(result);
      cout << "DEBUG: Take_object returned ***\n" << result << "***" << endl;
    }else{ // we have more than one objects, error!
      error = ERROR_UP_MOR;
      return NULL;
    }
    return result;
}






int process_object(char * arg){
    bool code = true;
    Object *o;
    char * old_version = NULL;
    o = new Object;
    code = o->scan(arg,strlen(arg));
    if(code){
      if(o->isDeleted){ 
        old_version = get_old_version(arg);
        if(old_version == NULL){ // the object doesn't exist in the db!
          return ERROR_UP_NSO; /* no such object */
        }else
        if(check_auth(NULL, old_version, o->type->getName()) == UP_AUTH_OK){ // check_auth will know that this is a deletion
          cout << "DEBUG: Will send the obj to be deleted" << endl;
          //send_object_db(arg,"DEL");
        }else{
          //auth failed !
          cout << error_msg << endl;
          return ERROR_UP_AUF; /* Auth failed */
        }
      }else{
        old_version = get_old_version(arg);
        if(check_auth(arg, old_version,o->type->getName() ) == UP_AUTH_OK){ // note: if the obj is to-be-created, 
                                          //        old_version becomes NULL
          cout << "DEBUG: Will send the obj to be added/updated" << endl;
          //send_object_db(arg,"ADD");
        }else{
          // auth failed !
          cout << error_msg << endl;
          return ERROR_UP_AUF; /* Auth failed */
        }
      }
    }else{// even if obj doesn't parse properly, it may be a legacy object
          // which the user wants to delete...
       return ERROR_UP_NIY; /* Not implemented yet */
    }
}






void main(int argc, char **argv, char **envp){
  schema.initialize();
  //init_and_set_options(argc, argv, envp);

  int count = 0;

  
  char *line;
  char *object = NULL;
  char *password = NULL;
  GSList *list_of_objects = NULL, *next = NULL;
  
  object = NULL;
  
  line = (char *)malloc(1024);

  while(fgets(line, 1024,stdin ) != NULL){
    /* first, if it is a pasword, save it, but do not regard it as an attrib */ 
    if(strstr(line, "password:") == line){
      cout << "This is a password" << endl;
      free(password);
      password = strdup(line + strlen("password:"));
      continue;
    }
    if(strlen(line) == 1){
       cout << endl;
       if(object != NULL){
         cout << "The object was" << endl << object << endl;
         list_of_objects = g_slist_append(list_of_objects, object);
         //free(object);
         object = NULL;
       }
    }else{
      /* if the line contains only the EOL char */
      if(object == NULL && strlen(line) != 1){
        object = (char *)malloc(strlen(line));
        object = strdup(line);
      }
      else{
        object = (char *)realloc(object, strlen(object) + strlen(line));
        object = strcat(object, line);
      }
    }
      
  }

  if(password != NULL){
    password = g_strstrip(password);
    cout << "This was the password: " << password << endl;
  }

  cout << "Now, printing out the objects in the list " << endl;
  next = list_of_objects;
  for( next = list_of_objects; next != NULL ; next = g_slist_next(next) ){
    cout << "=====" << endl << "Got a member" << endl;
    cout << (char *)next->data << endl;
    process_object((char *)next->data);
    cout << "=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=" << endl;
  }


}
