/***************************************
  $Revision: 1.10 $

  UP external syntax checks

  Status: NOT REVIEWED, NOT TESTED

  Author(s):       Engin Gunduz

  ******************/ /******************
  Modification History:
        engin (15/12/2000) Created.
  ******************/ /******************
  Copyright (c) 2001                              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 "rpsl/object.hh"
#include "UP_extrnl_syntax.h"
#include "dbupdate.h"

#define UP_DATE_OK      0
#define UP_DATE_SYNERR  1
#define UP_DATE_FUTURE  2
#define UP_DATE_TOOSMALL        3
#define UP_DATE_INVMONTH        4
#define UP_DATE_INVDAY  5
#define UP_DATE_WRONGFORMAT 6
#define UP_DATE_NOK    7



char * up_date_errmsgs[]={

  "OK",
  "Syntax error in date of 'changed' attribute",
  "Date in the future in 'changed' attribute",
  "Date is older than the database itself in 'changed' attribute",
  "Invalid month in date in 'changed' attribute",
  "Invalid day in date in 'changed' attribute",
  "Date must be in YYYYMMDD format in 'change' attribute",
  "Syntax error in date of 'changed' attribute"
};


extern int tracing;

extern char * fingerprint;
extern char * keyowner;



/* obtains a list of dates in the given 
   list of attributes (GSList of attribute_struct) */
GSList * up_get_dates(GSList * attribute_list){

  GSList * next;
  char * temp, * str; 
  int i;
  GSList * list = NULL;

  for( next = attribute_list; next != NULL ; next = g_slist_next(next) ){
    /* is this a 'changed' attribute? */
    if(strcmp((char *)(((attribute_struct *)(next->data))->type), "changed") == 0){
      temp = strdup(((attribute_struct *)(next->data))->content);

      /* delete the part after '#', inclusive */
      if(index(temp,'#') != NULL){
        temp[index(temp, '#') - temp] = '\0';
      }
      /* replace \n, \r & \t's with " " */
      for(i = 0; i < strlen(temp) ;i++){
        if(temp[i] == '\n' || temp[i] == '\r' || temp[i] == '\t' ){
          temp[i] = ' ';
        }
      }
      g_strstrip(temp);
      /* delete multiple spaces */
      str = (char *)malloc(strlen(temp) + 1);
      up_string_pack(str, temp);
      free(temp); 

      /* now, we have the 'changed' attribute's content in "normalized" form 
         We are sure it contains a date. So, it must be the second (and last)
         word in the attrib. */
      assert(index(str,' ') != NULL);
      temp = (char *)malloc(strlen(str) - (index(str,' ') - str ));
      temp = strncpy(temp, index(str,' ') + 1, strlen(str) - (index(str,' ') - str ) - 1);
      temp[strlen(str) - (index(str,' ') - str ) - 1] = '\0'; /* NULL terminate it */
      list = g_slist_append (list, temp);   
    }
  }
  
  return list;
}




/* Does the 'changed' attribute we got have a date already?
   Returns 1 if it does, 0 if not. */
int up_changed_has_date(char * arg){
  
  int i;
  char * str;
  char * temp;
  
  str = strdup(arg);

  /* cut off the EOL comments */
  if(index(str, '#')){
    str[index(str, '#') - str - 1 ] = '\0';
  }
  
  /* replace \n, \r & \t's with " " */
  for(i = 0; i < strlen(str) ;i++){
    if(str[i] == '\n' || str[i] == '\r' || str[i] == '\t' ){
      str[i] = ' ';
    }
  }
  g_strstrip(str);
  /* delete multiple spaces */
  temp = (char *)malloc(strlen(str) + 1);
  up_string_pack(temp, str);
  
  free(str);
  str = temp;
  
  /* now, if there is still a white space, then we have a date in the string
     (it has to be something like "ripe-dbm@ripe.net 20001210") */
  if(index(str, ' ') != NULL){
    return 1; 
  }else{
    return 0;
  }
}




/* supplies the current date in YYYYMMDD format (for example 20011010) */
char * UP_get_current_date(){
  /* We will use Glib's functions here */

  char * date;
  struct tm * time_struct;
  
  time_t * time_loc;

  time_loc = (time_t *)malloc(sizeof(time_t));
  time(time_loc);
  
  time_struct = localtime(time_loc);


  date = (char *)malloc(9);
  sprintf(date, "%04i%02i%02i", 
          time_struct->tm_year + 1900, 
          time_struct->tm_mon + 1,
          time_struct->tm_mday);
  return date;
}








/* int up_add_dates: adds dates to 'changed' attributes which 
     are missing one.
     Returns 1 if no problems encountered
     Returns 0 if a problem encountered, and the error string is set */
int up_add_dates(GSList * attribute_list, char ** warning_str, char ** error_str){
  
  GSList * next;
  char * attribute, * current_date;
  int count_no_date = 0; 
  char * temp;
  
  *warning_str = NULL;
  *error_str   = NULL;

  /* get the current date in YYYYMMDD format (for example 20011010) */
  current_date = UP_get_current_date();
  
  for( next = attribute_list; next != NULL ; next = g_slist_next(next) ){
    /* is this a 'changed' attribute? */
    if(strcmp((char *)(((attribute_struct *)(next->data))->type), "changed") == 0){
      /* if this attribute does not have a date in it, add it. Also add 
          a warning message about this */
      if( !up_changed_has_date((char *)(((attribute_struct *)(next->data))->content))){
        count_no_date++;
        attribute = (char *)(((attribute_struct *)(next->data))->content);
        temp = (char *)malloc(strlen(attribute) + strlen(current_date) + 2 );
        if(index(attribute, '#')){/* cut off the EOL comments */
          attribute[index(attribute, '#') - attribute - 1] = '\0';
        }
        sprintf(temp, "%s %s", attribute, current_date);
        ((attribute_struct *)(next->data))->content = temp;
        free(attribute);
        /* add a warning message */
        if( *warning_str == NULL){
          *warning_str = (char *)malloc(strlen("WARNING  date '' added to 'changed' attribute") + 9 );
          sprintf(*warning_str, "WARNING  date '%s' added to 'changed' attribute", current_date);
        }else{
          temp = (char *)malloc(strlen(*warning_str) + 1 
                                       + strlen("WARNING  date '' added to 'changed' attribute") + 9 );
          sprintf(temp, "%s\nWARNING  date '%s' added to 'changed' attribute", 
                              *warning_str, current_date);
          free(*warning_str);
          *warning_str = temp; 
        }
      }
    }
  }
  
  if(count_no_date > 1){ 
    *error_str = strdup("***Error: More than one 'changed' attributes without dates");
    return 0;
  }else{
    return 1;
  }
  

}






/* Checks the order of dates in the given list. 
   If they are in order, returns 1, 
   if not, returns 0 */
int up_check_date_order(GSList * list){
   
  GSList * next;
  char * previous;

  /* if list is empty, return 1 immediately */
  if(list == NULL){
    return 1;
  }

  /* initialize the 'previous' date */
  previous = strdup("00000000");
   
  for( next = list; next != NULL ; next = g_slist_next(next)){
    assert((next->data) != NULL);
    /* if the new date is smaller than the previous */
    if(strcmp((char *)(next->data), previous) < 0 ){
      free(previous);
      return 0;
    }
    free(previous);
    previous = strdup((char *)(next->data));
  }
   
  free(previous);
  /* Reached the end, without finding out-of-order date. Return 1, then */
  return 1; 
   
}





/* up_check_date: checks the syntax of the date, given as the only
   argument (char *). The argument is checked if it is in YYYYMMDD
   format, and returns an error code accordingly */
int up_check_date(const char * arg){

  int date_int; /* integer representation of the date (arg) */
  char * current_date;
  int year, month, day; /* integers for the components of the date */


  errno = 0;
  date_int = atoi(arg);

      
  if(errno != 0){/* there was an error in the conversion, syntax error */
    return UP_DATE_SYNERR;
  }
    
  /* wrong format */
  if(date_int <= 10000000 ){ /* the date is not in YYYYMMDD format */
    return UP_DATE_WRONGFORMAT;
  }


  /* check if it is too small */  
  if(date_int <= 19880000 ){ /* the date is older than the DB itself! */
    return UP_DATE_TOOSMALL;
  }

  /* check if it is too big */
  if(date_int >= 100000000 ){/* too big: syntax error */
    return UP_DATE_SYNERR;
  }

  /* and now check year, month and day components */
  year = date_int / 10000;
  month = (date_int - (year * 10000) ) / 100;
  day = (date_int % 100);
  
  /* check year */
  if(year < 1988 ){
    return UP_DATE_TOOSMALL;
  }

  /* check month */
  if(month < 1 || month > 12){
    return UP_DATE_INVMONTH;
  }

  /* check day */
  if(day < 1 || day > 31){
    return UP_DATE_INVDAY;
  }

  switch( month ){
    case 1: case 3: case 5: case 7:
    case 8: case 10: case 12:
         if (day > 31){
           return UP_DATE_INVDAY;
         };
         break;
    case 2: 
         if ( (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)) && (day > 29 )){ /* leap year */
           return UP_DATE_INVDAY;
         }else if( (!(year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))) && (day > 28) ){/* non-leap year */
           return UP_DATE_INVDAY;
         };
         break;
    case 4: case 6: case 9: case 11:
         if (day > 30){
           return UP_DATE_INVDAY;
         };
         break;
    default: return UP_DATE_INVMONTH;

  }

  /* check if the arg is in the future or not */
  current_date = UP_get_current_date();
  if(strcmp(current_date, arg) < 0 ){/* arg is in the future */
    free(current_date);
    return UP_DATE_FUTURE;
  }
  free(current_date);
  
    
  return UP_DATE_OK;
  
}










/* Checks the syntax of the dates in the list */
date_syntax_struct * up_check_dates_syntax(GSList * list){
   
  GSList * next;
  char * previous;
  date_syntax_struct * result;
  int res;

  /* initialize the result struct */
  result = (date_syntax_struct *)malloc(sizeof(date_syntax_struct));
  result->result = UP_DATE_OK;
  result->error_str = NULL;

  /* if list is empty, return immediately */
  if(list == NULL){
    return result;
  }

  /* loop through the members of the list, check each of them */
  for( next = list; next != NULL ; next = g_slist_next(next)){
    assert((next->data) != NULL);
    
    /* check the date */
    res = up_check_date((char *)next->data);
    switch (res){
      case UP_DATE_OK: break;
      
      case UP_DATE_FUTURE:
      case UP_DATE_TOOSMALL:
      case UP_DATE_INVDAY:
      case UP_DATE_INVMONTH:
      case UP_DATE_WRONGFORMAT:

             if(result->error_str == NULL){
               result->error_str = (char *)malloc(strlen("***Error: ") + strlen(up_date_errmsgs[res]) 
                                                  + strlen(": ") + strlen((char *)next->data) + 1);
               sprintf(result->error_str, "***Error: %s: %s", up_date_errmsgs[res],
                       (char *)next->data);
             }else{
               result->error_str = (char *)realloc(result->error_str, strlen(result->error_str) + 1 
                                                   + strlen("***Error: ") + strlen(up_date_errmsgs[res]) 
                                                   + strlen(": ") + strlen((char *)next->data) + 1);
               sprintf(result->error_str, "%s\n***Error: %s: %s",
                       result->error_str, up_date_errmsgs[res], (char *)next->data);
             }
             result->result = UP_DATE_NOK; /* Not OK */

             break;
             
             
      case UP_DATE_SYNERR: /* syntax error in the date */

      default:
             if(result->error_str == NULL){
               result->error_str = (char *)malloc(strlen("***Error: ") + strlen(up_date_errmsgs[UP_DATE_SYNERR]) 
                                                  + strlen(": ") + strlen((char *)next->data) + 1);
               sprintf(result->error_str, "***Error: %s: %s", up_date_errmsgs[UP_DATE_SYNERR],
                       (char *)next->data);
             }else{
               result->error_str = (char *)realloc(result->error_str, strlen(result->error_str) + 1 
                                                   + strlen("***Error: ") + strlen(up_date_errmsgs[UP_DATE_SYNERR]) 
                                                   + strlen(": ") + strlen((char *)next->data) + 1);
               sprintf(result->error_str, "%s\n***Error: %s: %s",
                       result->error_str, up_date_errmsgs[UP_DATE_SYNERR], (char *)next->data);
             }
             result->result = UP_DATE_NOK; /* Not OK */
             break;
    }

  }
  
  
  return result;
   
}










/* void up_check_changed_attr 
   checks the order of dates in the 'changed' attributes */
void up_check_changed_attr(Object * obj, char * obj_text, GSList * attribute_list, 
                           external_syntax_struct * result){

  GSList * date_list;
  int res;
  char ** warning, **error;
  char * temp;
  date_syntax_struct * date_check_res;


  warning = (char **)malloc(sizeof(char **));
  error   = (char **)malloc(sizeof(char **));
   

  /* Now, add dates to the "changed" attributes */
  res = up_add_dates(/*changed_list*/attribute_list, warning, error);
  if(!res){
    /* so, add the error string to result's error string */
    if(result->error_str == NULL){
      result->error_str = strdup(*error);
    }else{
      temp = (char *)malloc(strlen(result->error_str) + strlen(*error) + 2);
      sprintf(temp, "%s\n%s", result->error_str, *error);
      free(result->error_str);
      result->error_str = temp;
    }
  }

  /* and get the list of dates, we must check their order */
  date_list = up_get_dates(attribute_list);
  /* and check the order */ 
  res = up_check_date_order(date_list);
  if(!res){
    /* so, add the error string to result's error string */
    if(result->error_str == NULL){
      result->error_str = strdup("***Error: The dates in the 'changed' attributes should be in order");
    }else{
      temp = (char *)malloc(strlen(result->error_str) 
              + strlen("***Error: The dates in the 'changed' attributes should be in order") + 2);
      sprintf(temp, "%s\n%s", result->error_str, 
              "***Error: The dates in the 'changed' attributes should be in order");
      free(result->error_str);
      result->error_str = temp;
    }
    /* and here we have to change the result code of "result" here ... */
    switch(result->result){
      case UP_EXTSYN_OK:       result->result = UP_EXTSYN_ERR; break;
      case UP_EXTSYN_ERR:      result->result = UP_EXTSYN_ERR; break;
      case UP_EXTSYN_WARN:     result->result = UP_EXTSYN_ERR_WARN; break;
      case UP_EXTSYN_ERR_WARN: result->result = UP_EXTSYN_ERR_WARN; break;
      default: ;
    }
  }

  /* check the syntax of dates */
  date_check_res = up_check_dates_syntax(date_list);
  if(date_check_res->result != UP_DATE_OK){
    /* so, add the error string to result's error string */
    if(result->error_str == NULL){
      result->error_str = strdup(date_check_res->error_str);
    }else{
      temp = (char *)malloc(strlen(result->error_str) 
              + strlen(date_check_res->error_str) + 2);
      sprintf(temp, "%s\n%s", result->error_str, 
              date_check_res->error_str);
      free(result->error_str);
      result->error_str = temp;
    }
    /* and here we have to change the result code of "result" here ... */
    switch(result->result){
      case UP_EXTSYN_OK:       result->result = UP_EXTSYN_ERR; break;
      case UP_EXTSYN_ERR:      result->result = UP_EXTSYN_ERR; break;
      case UP_EXTSYN_WARN:     result->result = UP_EXTSYN_ERR_WARN; break;
      case UP_EXTSYN_ERR_WARN: result->result = UP_EXTSYN_ERR_WARN; break;
      default: ;
    }
    
  }
  
}





int up_check_an_auth_attr(const char * arg, char ** error){

  char * attr;
  int ret = 1;
  char * cryptpw, * key;


  attr = strdup(arg);
  /*  chop the whitespace in hte beginning and end */
  g_strstrip(attr);

  /* Convert to uppercase  */
  g_strup(attr);

  /* chop the EOL comment off */
  if(index(attr, '#') != NULL){
    attr[index(attr, '#') - attr ] = '\0';
  }

  if(strstr(attr, "MAIL-FROM ") == attr){
    /* It must have somethings after "MAIL-FROM ", which are
      supposed to be a regexp */
    if(strlen(attr) <= strlen("MAIL-FROM ")){
      ret = 0;
    }
  }else if(strstr(attr, "NONE") == attr){
    /* We mustn't have anything after "NONE"  */
    if(strlen(attr) != strlen("NONE")){
      *error = strdup("***Error: There mustn't be anything after NONE in 'auth' attribute");
      ret = 0;
    }
  }else if(strstr(attr, "CRYPT-PW ") == attr){
    /* The string after CRYPT-PW must be of length 13 and must consist of certain
       characters */
    cryptpw = strdup(attr + strlen("CRYPT-PW "));
    g_strstrip(cryptpw);
    if(strlen(cryptpw) != 13){
      *error = strdup("***Error: The crypted password must be 13-character long in 'auth' attribute");
      free(cryptpw);
      ret = 0;
    }
  }else if(strstr(attr, "PGPKEY-") == attr){
    /* The string after CRYPT-PW must be of length 13 and must consist of certain
       characters */
    key = strdup(attr + strlen("PGPKEY-"));
    g_strchomp(key);
    if(strlen(key) != 8){
      *error = strdup("***Error: The PGP key must be 8-character long in 'auth' attribute");
      
      free(key);
      ret = 0;
    }

  }else{
    *error = strdup("***Error: 'auth' attribute must start with MAIL-FROM, NONE, PGPKEY- or CRYPT-PW");
    ret = 0;
  }

  free(attr);
  return ret;
}







/* void up_check_auth_attr 
   checks the syntax of 'auth' attributes */
void up_check_auth_attr(Object * obj, char * obj_text, GSList * attribute_list, external_syntax_struct * result){

  int res;
  char **error;
  char * temp;
  GSList * next;

  error   = (char **)malloc(sizeof(char **));

  /* loop in the attribute_list, find the 'auth' attribs, and check their syntax */
  for( next = attribute_list; next != NULL ; next = g_slist_next(next) ){
    /* is this an 'auth' attribute? */
    if(strcmp((char *)(((attribute_struct *)(next->data))->type), "auth") == 0){
      /* chech its syntax */
      res = up_check_an_auth_attr((char *)(((attribute_struct *)(next->data))->content), error);
      if(!res){
        /* so, add the error string to result's error string */
        if(result->error_str == NULL){
          result->error_str = strdup(*error);
        }else{
          temp = (char *)malloc(strlen(result->error_str) 
                  + strlen(*error) + 2);
          sprintf(temp, "%s\n%s", result->error_str, 
                  *error);
          free(result->error_str);
          result->error_str = temp;
        }
        /* and here we have to change the result code of "result" here ... */
        switch(result->result){
          case UP_EXTSYN_OK:       result->result = UP_EXTSYN_ERR; break;
          case UP_EXTSYN_ERR:      result->result = UP_EXTSYN_ERR; break;
          case UP_EXTSYN_WARN:     result->result = UP_EXTSYN_ERR_WARN; break;
          case UP_EXTSYN_ERR_WARN: result->result = UP_EXTSYN_ERR_WARN; break;
          default: ;
        }
      }
    }
  }
}


/* performs a simple check on a inetnum attribute. Assumes that
   the RPSL parser has already checked it. Tries to see if the attr
   is a range or not  */
int up_check_an_inetnum_attr(const char * arg){
  

  char * str;
  char * pos;

  str = strdup(arg);

  while((pos = index(str, '\n')) != NULL){
    *pos = ' ';
  }

  /* strip off the comment */
  if((pos = index(str, '#')) != NULL){
    *pos = '\0';
  }
   
   
  /* Most of the syntax check is done by RPSL parser. We only need to check
     that the argument is a _range_ of IPv4 addresses. So it suffices to
     check the existence of '-' in the arg */
  if(index(str, '-') != NULL){
    
    free(str);
    return 1;
    
  }else{

    free(str);
    return 0;

  }
}



/* void up_check_inetnum_attr 
   checks the syntax of 'inetnum' attributes 
   (this is required to reject inetnums in the form of 
   "inetnum: 23.23.23.23". RPSL parser unfortunately
   passes them although we define them as a range of ipv4 addreses.) */
void up_check_inetnum_attr(Object * obj, char * obj_text, GSList * attribute_list, external_syntax_struct * result){

  int res;
  char * temp;
  GSList * next;


  /* loop in the attribute_list, find the 'inetnum' attribs, and check their syntax */
  for( next = attribute_list; next != NULL ; next = g_slist_next(next) ){
    /* is this an 'inetnum' attribute? */
    if(strcmp((char *)(((attribute_struct *)(next->data))->type), "inetnum") == 0){
      /* chech its syntax */
      res = up_check_an_inetnum_attr((char *)(((attribute_struct *)(next->data))->content));
      if(!res){
        /* so, add the error string to result's error string */
        if(result->error_str == NULL){
          result->error_str = strdup("***Error: inetnum must be a range of IPv4 address\n");
        }else{
          temp = (char *)malloc(strlen(result->error_str) 
                  + strlen("***Error: inetnum must be a range of IPv4 address\n") + 2);
          sprintf(temp, "%s\n%s", result->error_str, 
                  "***Error: inetnum must be a range of IPv4 address\n");
          free(result->error_str);
          result->error_str = temp;
        }
        /* and here we have to change the result code of "result" here ... */
        switch(result->result){
          case UP_EXTSYN_OK:       result->result = UP_EXTSYN_ERR; break;
          case UP_EXTSYN_ERR:      result->result = UP_EXTSYN_ERR; break;
          case UP_EXTSYN_WARN:     result->result = UP_EXTSYN_ERR_WARN; break;
          case UP_EXTSYN_ERR_WARN: result->result = UP_EXTSYN_ERR_WARN; break;
          default: ;
        }
      }
    }
  }
}







/* GSList * up_add_keycert_attrs
   adds the generated attrs of key-cert objects */
GSList * up_add_keycert_attrs(Object * obj, char * obj_text, GSList * attribute_list, external_syntax_struct * result){

  int res;
  char * type, * content;
  char **error;
  char * temp;
  GSList * new_list;
  attribute_struct * attribute;
  
  new_list = attribute_list;


  /* if this is a key-cert object */
  if(strcmp(obj->type->getName(), "key-cert") == 0){
      content = strdup("PGP");
      type = strdup("method");
      attribute = (attribute_struct *)malloc(sizeof(attribute_struct)); 
      attribute->content = content;
      attribute->type = type;
      new_list = g_slist_insert(new_list, attribute, 1);

      content = strdup(keyowner);
      type = strdup("owner");
      attribute = (attribute_struct *)malloc(sizeof(attribute_struct)); 
      attribute->content = content;
      attribute->type = type;
      new_list = g_slist_insert(new_list, attribute, 2);

      content = strdup(fingerprint);
      type = strdup("fingerpr");
      attribute = (attribute_struct *)malloc(sizeof(attribute_struct)); 
      attribute->content = content;
      attribute->type = type;
      new_list = g_slist_insert(new_list, attribute, 3);
      
  }

  return new_list;

}










/* Constructs a list of all attributes of an object, and returns it
   as a list of attribute_struct */
GSList * up_get_attribute_list(Object * o, char * text){

  char * value = NULL;
  char * type = NULL;
  Attr *attr;
  GSList *list_of_attributes = NULL;
  attribute_struct * attribute;
  
  for(attr = o->attrs.head(); attr; attr = o->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';
      if(tracing) {
        cout << "TRACING: up_get_attributes: adding " << g_strstrip(value) << endl;
      }
      attribute = (attribute_struct *)malloc(sizeof(attribute_struct)); 
      attribute->content = value;
      type = strdup(attr->type->name());
      g_strdown(type);
      attribute->type = type;
      list_of_attributes = g_slist_append(list_of_attributes, attribute);
  }

  
  return list_of_attributes; 

}




/* up_reconstruct_object: Reconstructs the object's text representation
     using the attribute list */
void up_reconstruct_object(GSList * attr_list, external_syntax_struct *result){

  char * recons_obj = NULL;
  char * temp;
  char * content, * type;
  GSList * next;




  for( next = attr_list; next != NULL ; next = g_slist_next(next) ){

    content = strdup((char *)(((attribute_struct *)(next->data))->content));
    type = strdup((char *)(((attribute_struct *)(next->data))->type));

    if(tracing){
      printf("TRACING: up_reconstruct_object:[%s:%s]\n", type, content);
    }
    
    if(recons_obj == NULL){
      recons_obj = (char *)malloc(14 + strlen(content) + 1 );
      /* trim the white spaces in the beginning */
      g_strchug(content);

      /* add ':' at the end of 'type' */
      temp = (char *)malloc(strlen(type) + 2);
      sprintf(temp, "%s:", type);
      free(type);
      type = temp;

      sprintf(recons_obj, "%-14s%s", type, content);
    }else{
      /* trim the white spaces in the beginning */
      g_strchug(content);

      /* add ':' at the end of 'type' */
      temp = (char *)malloc(strlen(type) + 2);
      sprintf(temp, "%s:", type);
      free(type);
      type = temp;
      
      temp = (char *)malloc(strlen(recons_obj) + 14 + strlen(content) + 3 );
      sprintf(temp, "%s\n%-14s%s", recons_obj, type, content);
      free(recons_obj);
      recons_obj = temp;
    }
    
  }
  
  result->new_obj = recons_obj;
  
}




/*  UP_check_external_syntax: Checks the syntax of attributes which are not checked
       by RAToolSet syntax checker. */
external_syntax_struct * UP_check_external_syntax(Object * arg, char * obj_text){
  
  external_syntax_struct *result;
  GSList * attribute_list;
  
 


  result = (external_syntax_struct *)malloc(sizeof(external_syntax_struct));

  /* initialize the struct */
  result->result = 0;
  result->error_str = strdup(""); 
  result->warning_str = strdup("");

  /* get a list of all attributes */
  attribute_list = up_get_attribute_list(arg, obj_text);

  up_check_changed_attr(arg, obj_text, attribute_list, result);
  up_check_auth_attr   (arg, obj_text, attribute_list, result);  
  up_check_inetnum_attr(arg, obj_text, attribute_list, result);
  
  up_reconstruct_object(attribute_list, result);

  if(tracing){
    printf("TRACING: UP_check_external_syntax: the reconstructed object is=[%s]\n", result->new_obj);
    printf("TRACING: UP_check_external_syntax: ... and the result code is=[%i]\n", result->result);
  }

  return result;  
}





/* generates the "generated" attributes of a key-cert object. Returns the 
   new object as a char * */
char * UP_generate_kc_attrs(Object * arg, char * obj_text){
  
  external_syntax_struct *result;
  GSList * attribute_list;
  
 


  result = (external_syntax_struct *)malloc(sizeof(external_syntax_struct));

  /* initialize the struct */
  result->result = 0;
  result->error_str = strdup(""); 
  result->warning_str = strdup("");

  /* get a list of all attributes */
  attribute_list = up_get_attribute_list(arg, obj_text);

  up_check_changed_attr(arg, obj_text, attribute_list, result);
  //up_check_auth_attr   (arg, obj_text, attribute_list, result);  
  attribute_list = up_add_keycert_attrs (arg, obj_text, attribute_list, result); 

  
  up_reconstruct_object(attribute_list, result);

  if(tracing){
    printf("TRACING: UP_check_external_syntax: the reconstructed object is=[%s]\n", result->new_obj);
    printf("TRACING: UP_check_external_syntax: ... and the result code is=[%i]\n", result->result);
  }

  return result->new_obj;  
}

