/* 
 * $Id: syntax_attrs.c,v 1.21 2002/10/17 20:22:10 ljb Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <regex.h>
#include <sys/socket.h>
#include <time.h>
#include <irr_rpsl_check.h>
#include <pipeline_defs.h>
#include <sys/stat.h>
#include <pgp.h>

extern char *attr_name[];
extern regex_t re[];
extern char *countries[];
static const char tmpfntmpl[] = "/var/tmp/irrsyntax.XXXXXX";

/* JW: must define later 
int F_RO = -1, F_PN = -1;
*/


int xx_set_syntax (char *target, char *s) {
  char *p;
  char *q = strdup (s);
  int ret_val = 0;

  p = strtok (q, ":");
  while (p != NULL) {
    if (!strcasecmp (p, target)) {
      ret_val = 1;
      break;
    }
    p = strtok (NULL, ",");
  }
  free (q);

  return ret_val;
}

/* -------------individual field check functions ------------------*/

int country_syntax (char *country, parse_info_t *obj) {

  if (is_country (country, countries) >= 0)
    return 1;

  error_msg_queue (obj, "Unknown country", ERROR_MSG);  
  return 0;
}

/* Given an inetnum address range (ie, inetnum: a1 - a2 or a1 > a2)
 * determine if a1 is greater than a1.
 *
 * Return:
 *   1 if a1 is numerically <= a2 (ie, no errors)
 *   0 if a1 is numerically greater than a2 (ie, an error).
 */
int inetnum_syntax (parse_info_t *obj, char *prefix1, char *prefix2) {
  u_char dst1[4], dst2[4];
  u_int val1, val2;
  int i;

  if (irrd_inet_pton (AF_INET, prefix1, dst1) <= 0) {
    error_msg_queue (obj, "Malformed address in start of range", ERROR_MSG);
    return 0;
  }

  if (irrd_inet_pton (AF_INET, prefix2, dst2) <= 0) {
    error_msg_queue (obj, "Malformed address in end of range", ERROR_MSG);
    return 0;
  }
  
  val1 = val2 = 0;
  for (i = 0; i < 4; i++) {
    val1 |= dst1[i] << (24 - (i * 8));
    val2 |= dst2[i] << (24 - (i * 8));
  }

  if (val1 > val2) {
    error_msg_queue (obj, "Invalid address range", ERROR_MSG);
    return 0;
  }

  return 1;
}


/* ud - delete
 *
 * Return codes:
 *  0 means syntax failed, ie bad syntax
 *  1 means a warning was given
 *  2 means good syntax
 *  an empty attr generates a warning and
 *  is not an error.
 */
int delete_syntax (char *del, parse_info_t *obj) {

  if (obj->op == NULL)
    obj->op = strdup (DEL_OP);
  
  return 2;
}

/* password
 *
 * Return codes:
 *  0 means syntax failed, ie bad syntax
 *  1 means a warning was given
 *  2 means good syntax
 *  an empty attr is silently ignored.
 */
int password_syntax (char *del, parse_info_t *obj) {
  char *p, *q;

  if (del == NULL)
    return 1;

  if ((p = strchr (del, '\n')) != NULL)
    *p = '\0';

  p = q = del;

  if (irrcheck_find_token (&p, &q) < 0)
    return 1;
  
  if (obj->password == NULL)
    obj->password = strdup (p);
  
  return 2;
}


/*
 * so - source
 */
void source_syntax (char *source_name, parse_info_t *obj) {
  source_t *src;
  extern config_info_t ci;

  if ((src = is_sourcedb (source_name, ci.srcs)) != NULL) {
    if (!src->authoritative)
      error_msg_queue (obj, "Non-authoritative database", ERROR_MSG);      
  }
  else
    error_msg_queue (obj, "Unknown database", ERROR_MSG);      
}



/*
 * em - e-mail
 * gd - guardian
 * mn - mnt-nfy
 * ny - notify
 * om - op-mail
 * dt - upd-to
 */
int email_syntax (char *email_addr, parse_info_t *obj) {
  char *p;

  /*if (verbose) fprintf (dfile, "JW: email_syntax(%s)\n", email_addr);*/
  if ((p = strchr (email_addr, '@')) == NULL) {
    error_msg_queue (obj, "Missing '@' in email address", ERROR_MSG);
    return 0;
  }

  if (strchr (p + 1, '@') != NULL) {
    error_msg_queue (obj, "Multiple '@' found in email address", ERROR_MSG);
    return 0;
  }

  if (regexec (&re[RE_EMAIL3], email_addr, (size_t) 0, NULL, 0)  ||
      (regexec (&re[RE_EMAIL1], email_addr, (size_t) 0, NULL, 0) &&
       regexec (&re[RE_EMAIL2], email_addr, (size_t) 0, NULL, 0))) {
    error_msg_queue (obj, "Malformed RFC822 email address", ERROR_MSG);       
    return 0;
  }

  return 1;
}

void regex_syntax (char *reg_exp, parse_info_t *obj) {
  regex_t comp_re;
  
  if (regcomp (&comp_re, reg_exp, REG_EXTENDED|REG_NOSUB) != 0) {
    error_msg_queue (obj, "Illegal regular expression", ERROR_MSG);     
    return;
  }
  
  regfree(&comp_re);
}

void cryptpw_syntax (char *cryptpw, parse_info_t *obj) {
  
  if (regexec(&re[RE_CRYPT_PW], cryptpw, (size_t) 0, NULL, 0))
      error_msg_queue (obj, "Illegal encrypted password ([./0-9A-Za-z]{13})", ERROR_MSG);
}

/* Return today's date in integer format, 
 * eg, 19991122
 */
int todays_date () {
  time_t now;
  struct tm *tmptr;
  
  now = time (NULL);
  tmptr  = localtime (&now);

  return ((1900 + tmptr->tm_year) * 10000) + (++tmptr->tm_mon * 100) + tmptr->tm_mday;
}

/* Given a 'date', deterimine if it is in the future.
 * 
 * Return:
 *   A non-NULL string date with today's date if the input
 *   date is in the future.
 *
 *   Otherwise return NULL
 */
char *date_in_future (char *date) {
  int intdate1 = 0, intdate2;
  char strdate[10];

  sscanf (date, "%d", &intdate1);
  if (intdate1 == 0)
    return NULL;

  intdate2 = todays_date ();

  if (intdate1 > intdate2) {
    sprintf (strdate, "%d", intdate2);
    return strdup (strdate);
  }

  return NULL;
}

/* Given a 'date' (ie, YYMMDD) make sure the date is in proper
 * format and syntax.  YY must be >= 1988.  If the date is
 * in the future, change the date to today's date and return 
 * the correct date.  Skip the future check if 'skip_future_check' is set
 * (ie, withdrawn: attr)
 *
 * Return:
 *   NULL if no errors in date and date is not in the future
 *   today's date if no errors in date and date is in the future
 */
char *date_syntax (char *date, parse_info_t *obj, int skip_future_check) {
  char year[5];
  char month[3];
  char day[3];
  char strdate[9];
  char *now;
  int errors;


  /* ensure the date is 6 char's long and all numbers */
  if (regexec(&re[RE_DATE], date, (size_t) 0, NULL, 0)) {
      error_msg_queue (obj, "Malformed date (/^YYYYMMDD$/)", WARN_OVERRIDE_MSG);
      error_msg_queue (obj, "Changing to today's date", WARN_OVERRIDE_MSG);
      sprintf (strdate, "%d", todays_date ());
      return strdup (strdate);

    /*
	error_msg_queue (obj, "Malformed date (/^YYYYMMDD$/)", ERROR_MSG);
	return NULL;
    */
  }

  errors = 0;

  /* check year part */
  strncpy (year, date, 4);
  year[4] = '\0';

  if (strncmp (year, "1988", 4) < 0) {
      error_msg_queue (obj, "Year part of date is too old (YYYYMMDD)", ERROR_MSG);    
      errors++;
  }

  /* check month part */
  strncpy (month, &date[4], 2);
  month[2] = '\0';

  if ((strncmp (month, "01", 2) < 0) ||
      (strncmp (month, "12", 2) > 0)) {
      error_msg_queue (obj, "Syntax error in month part of date (YYYYMMDD)", ERROR_MSG);
      errors++;
  }

  /* check day part */
  strncpy (day, &date[6], 2);
  day[2] = '\0';

  if ((strncmp (day, "01", 2) < 0) ||
      (strncmp (day, "31", 2) > 0)) {
      error_msg_queue (obj, "Syntax error in day part of date (YYMMDD)", ERROR_MSG);
      errors++;
  }

  if (errors || skip_future_check)
    return NULL;

  if ((now = date_in_future (date)) != NULL) {
    /*
    error_msg_queue (obj, "Date is in the future, changed to today's date", WARN_OVERRIDE_MSG);
    */
    return now;
  }

  return NULL;
}

/* Return a time interval component adding or removing leading 0's as
 * necessary.
 *
 * Function will return the time value component in (outs) if no (ins)
 * can be represented in a field of (width) characters.  Otherwise
 * (outs) is not changed and an error message will be registered.
 * 
 * Input:
 *  -data struct to allow error messages to be created (obj)
 *  -string length of the time component (width)
 *  -error message to be diplayed if the time component cannot
 *   be trimmed to the proper length (err_msg)
 *  -input time component (ins)
 *  -return value time component in a string (width) characters long (outs)
 *
 * Return:
 *  -time component in a string (width) characters long if there were no errors
 *  -void (ie, do not change the (outs) value)
 */
void ti_check (parse_info_t *obj, int width, char *err_msg, char *ins, char *outs) {
  int i, j;
  char *p;

  /* time value is too short, pad with '0's */
  if ((i = strlen (ins)) < width) {
    sprintf (outs, "%.*s%s", (width - i), "0000", ins);
    return;
  }

  /* time value is too long,
   * see if removing leading '0's will solve */
  if (i > width) {
    for (p = ins; *p == '0'; p++);
    j = (int) (p - ins);
    if ((i - j) <= width)
      sprintf (outs, "%.*s%s", width - i + j, "0000", p);
    else
      error_msg_queue (obj, err_msg, ERROR_MSG);

    return;
  }

  /* good */
  strcpy (outs, ins);
}

/* Canonicalize and check for errors a time interval value.
 *
 * prescibed syntax:
 * 'dddd hh:mm:ss  eg, '0001 01:00:00'  
 *
 * 'dddd' are the day's, 'hh' are hours, 'mm' are minutes and 'ss' are seconds.
 * Function checks for 'hh' < 24, 'mm' and 'ss' < 60.  Function will attempt
 * to fix good values by adding or removing leading 0's as necessry. 
 *
 * Input:
 *  -data struct to allow error messages to be created (obj)
 *  -days value (days)
 *  -hours value (hours)
 *  -minutes value (mins)
 *  -seconds value (secs)
 *
 * Return:
 *  -a canonicalized time interval if no errors were found
 *  -NULL otherwise
 */
char *time_interval_syntax (parse_info_t *obj, char *days,
			    char *hours, char *mins, char *secs) {
  int j;
  char buf[16], d[5], h[3], m[3], s[3];

  /* days: '0000' */
  d[0] = '\0';
  ti_check (obj, 4, "Time interval (days) too long.  "
	    "Must be exactly 4 digits.  eg, '0010'", days, d);

  /* hours: '00' and < 24 */
  h[0] = '\0';
  sscanf (hours, "%d", &j);
  if (j > 23)
    error_msg_queue (obj, "Time inverval (hours) to large > 23", ERROR_MSG);
  else
    ti_check (obj, 2, "Time interval (hours) too long.  "
	      "Must be exactly 2 digits.  eg, '01'", hours, h);

  /* minutes: '00' and < 60 */
  m[0] = '\0';
  sscanf (mins, "%d", &j);
  if (j > 59)
    error_msg_queue (obj, "Time inverval (minutes) to large > 59", ERROR_MSG);
  else
    ti_check (obj, 2, "Time interval (minutes) too long.  "
	      "Must be exactly 2 digits.  eg, '30'", mins, m);

  /* seconds: '00' and < 60 */
  s[0] = '\0';
  sscanf (secs, "%d", &j);
  if (j > 59)
    error_msg_queue (obj, "Time inverval (seconds) to large > 59", ERROR_MSG);
  else
    ti_check (obj, 2, "Time interval (seconds) too long.  "
	      "Must be exactly 2 digits.  eg, '45'", secs, s);

  /* mal-formed time interval */
  if (*d == '\0' ||
      *h == '\0' ||
      *m == '\0' ||
      *s == '\0')
    return NULL;

  /* good time interval */
  sprintf (buf, "%s %s:%s:%s", d, h, m, s);

  /* clean up */
  free (days);
  free (hours);
  free (mins);
  free (secs);

  return strdup (buf);
}


void name_syntax (parse_info_t *obj, char *hdl) {
  char *p, *q, *r = NULL, c;
  int i;

  p = q = hdl;
  for (i = 0; irrcheck_find_token (&p, &q) > 0; i++, *q = c) {
    c = *q;
    *q = '\0';
    r = NULL;
    if (verbose) fprintf (dfile, "JW: field[%d]-(%s)\n", i, p);

    /* email names not allowed */
    if (strchr (p, '@') != NULL)
      error_msg_queue (obj, "Syntax error.  Looks like an email address", ERROR_MSG);
    /* no titles like Mr., Dr., sir, ... */
    else if (i == 0 && 
             !regexec(&re[RE_TITLES], p, (size_t) 0, NULL, 0))
      error_msg_queue (obj, "Syntax error.  Titles not allowed", ERROR_MSG);       
    /* check the basic structure and legal characters */
    else if (regexec(&re[RE_NAME], p, (size_t) 0, NULL, 0)) {
      error_msg_queue (obj, "Syntax error in name", ERROR_MSG);       
    }
    /* check for abbreviation on first and last name */
    else {
      r = p + strlen (p) - 1;
      if (i == 0 && *r == '.')
	error_msg_queue (obj, "Abbreviated first names not allowed", ERROR_MSG);
    }
  }

  if (r != NULL && *r == '.')
    error_msg_queue (obj, "Abbreviated last names not allowed", ERROR_MSG);

  /* legal names must have at least two components */
  if (i < 2)
      error_msg_queue (obj, "Names must have at least two components or bad handle", 
		       ERROR_MSG);
}

int is_nichdl (char *nichdl) {
  char *p;
  extern config_info_t ci;

  if (verbose) fprintf (dfile, "\n---JW: enter is_nichdl(%s): short circut, nichdl() always true!\n", nichdl);

  /* sanity check only */
  return !regexec(&re[RE_SANITY_HDL], nichdl, (size_t) 0, NULL, 0);
  
  /* lower case alpha not allowed */
  if (!regexec(&re[RE_LCALPHA], nichdl, (size_t) 0, NULL, 0)) {
    if (verbose) fprintf (dfile, "JW: found lower case, is not a handle (%s)!\n", nichdl);
    return 0;
  }
  
  /* check for auto-RIPE nic handle */
  if (!regexec(&re[RE_RIPE_HDL], nichdl, (size_t) 0, NULL, 0)) {
    if (verbose) fprintf (dfile, "JW: is a auto RIPE handle (%s)!\n", nichdl);
    return 1;
  }
  
  /* check for ARIN handles */
  if (!regexec(&re[RE_ARIN_HDL], nichdl, (size_t) 0, NULL, 0)) {
    if (verbose) fprintf (dfile, "JW: is a ARIN handle (%s)!\n", nichdl);
    return 1;
  }
  
  /* check for APNIC handle */
  if (!regexec(&re[RE_APNIC_HDL], nichdl, (size_t) 0, NULL, 0)) {
    if (verbose) fprintf (dfile, "JW: is a jpnic handle (%s)!\n", nichdl);
    return 1;
  }
  
  /* check for standard handle */
  if (!regexec(&re[RE_STD_HDL], nichdl, (size_t) 0, NULL, 0)) {
    if (verbose) fprintf (dfile, "JW: is a std handle (%s)!\n", nichdl);
    if ((p = strchr (nichdl, '-')) != NULL) {
      if (verbose) fprintf (dfile, "JW: checking suffix of std hdl (%s)...\n", p);
      p++;
      if (is_sourcedb (p, ci.srcs) != NULL) {
	if (verbose) fprintf (dfile, "JW: hdl-ret (is a sourcedb!\n");
        return 1;
      }
      
      if (is_country (p, countries) >= 0) {
	if (verbose) fprintf (dfile, "JW: hdl-ret (is a country!\n");
        return 1;
      }
      
      if (is_special_suffix (p)) {
	if (verbose) fprintf (dfile, "JW: hdl-ret (is a spec suffix!\n");
        return 1;
      }

      /* JMH - special case - see if it ends with -NIC */
      if (!strcmp (p, "NIC") && (p[3] == '\0')) {
	if (verbose) fprintf (dfile, "JMH: -NIC override\n");
	return 1;
      }
      
      /* JMH - we allow a handle with two '-'s in them so long as the
	 last one is -NIC */
      if ((p = strchr (p, '-')) != NULL) {
	p++;
	if (!strcmp (p, "NIC") && (p[3] == '\0')) {
	  if (verbose) fprintf (dfile, "JMH: -NIC override\n");
	  return 1;
	}
      }

    }
    else
      return 1;
  }

  return 0;
}

/*
 * ac - admin-c
 * ph - person
 * ro - role
 * ah - author
 * tc - tech-c
 * zc - zone-c
 */
void hdl_syntax (parse_info_t *obj, char *hdl) {
  char *tok_s, *p, *q;

  p = q = hdl;
  if (irrcheck_find_token (&p, &q) > 0) {
    if (verbose) fprintf (dfile, "JW: enter hdl_syntax(): %s %s\n", attr_name[obj->curr_attr], hdl);

    tok_s = p;
    /* see if we have a nic-hdl, illegal for role and person objs */
    if (irrcheck_find_token (&p, &q) < 0) {
      if (is_nichdl (tok_s)) {
	if (obj->curr_attr == F_RO || obj->curr_attr == F_PN)
	  error_msg_queue (obj, 
			   "Handles not allowed for \"person\" and \"role\" objects",
			   ERROR_MSG);
	return; /* nic-hdl's legal for admin-c, tech-c, zone-c, author */
      }
    }

    /* It's not a nic handle, so check for legal name syntax */
    name_syntax (obj, hdl);
  }
  else /* should never get here */
    error_msg_queue (obj, "Empty attribute removed", EMPTY_ATTR_MSG);
}

 /*
  * nh - nic-hdl
  */
void nichdl_syntax (char *nic_hdl, parse_info_t *obj) {

  if (!is_nichdl (nic_hdl))
    error_msg_queue (obj, "Syntax error in NIC handle", ERROR_MSG);
}

/*
 * cm - community
 */
void comm_syntax (char *comm_name, parse_info_t *obj) {
  
  if (regexec(&re[RE_COMM1], comm_name, (size_t) 0, NULL, 0)) {
      error_msg_queue (obj, "Syntax error in community name(/^[A-Z][A-Z0-9_-]+$/) ", 
		       ERROR_MSG);
      return;
  }

  if (!regexec(&re[RE_COMM2], comm_name, (size_t) 0, NULL, 0))
      error_msg_queue (obj, 
	"Community names cannot start with (/^(ANY|AND|OR|NOT|AS|LIM-)/)", ERROR_MSG);
}

/* Given a maintainer reference, '*mntner' (ie, a list member from a 'mnt-by'
 * attribute), check for names that begin with a reserved prefix (eg, 'rs-').
 *
 * Return:
 *    void
 *    side effect: if a name begins with a reserved prefex, send an error msg.
 */
void mb_check (parse_info_t *o, char *mntner) {
  char buf[MAXLINE];

  if (mntner == NULL)
    return;

  if (has_reserved_prefix (mntner)) {
    sprintf (buf, "Maintainer reference begins with a reserved prefix (%s)\n", mntner);
    error_msg_queue (o, buf, ERROR_MSG);
  }
}


/* Get the key fingerprint from the ring pointed to by the environment
 * variable 'PGPPATH' which should be set before calling this function.  
 * This function assumes that a new 'key-cert' object has been added to 
 * a temporary ring (ie, so there is a ring with a single key).  If there 
 * is no fingerprint or more than one then return an error return code.
 *
 * DSS/Diffie-Hellman keys require special processing since there is
 * a fingerprint for the DSS key (which is used for signing and the 
 * one we want) and the Diffie-Hellman key.  Here is an example:
 *

% pgpk +batchmode=1 -ll
Type Bits KeyID      Created    Expires    Algorithm       Use
pub   768 0x32ED1200 2000-06-30 ---------- DSS             Sign & Encrypt 
f20    Fingerprint20 = B512 5B8A BF23 EEE2 A10A  0A93 5451 7218 32ED 1200
sub   768 0x5B2AE285 2000-06-30 ---------- Diffie-Hellman                 
f20    Fingerprint20 = 39AA 19FC A6C7 03D3 0078  B368 257E D59F 5B2A E285
uid  Gerald A. Winters <gerald@merit.edu>
sig       0x32ED1200 2000-06-30 Gerald A. Winters <gerald@merit.edu>

 * We want the fingerprint after the 'pub', which is the first one.  After
 * the 'sub' we don't care and ignore any more fingerprints.
 *
 * Note for RSA keys we do not have this sitution.
 *
% pgpk -ll
Type Bits KeyID      Created    Expires    Algorithm       Use
pub   768 0x8938AA1B 2000-06-30 ---------- RSA             Sign & Encrypt 
f16    Fingerprint16 = 6A E5 51 2B F8 67 F6 72  A6 8E 1E 05 13 6F BD AB
uid  Gerald A. Winters <gerald@merit.edu>
sig       0x8938AA1B 2000-06-30 Gerald A. Winters <gerald@merit.edu>

 *
 * Return:
 *
 *    0 if a single key fingerprint was extracted from the ring with no errors
 *    1 otherwise
 */
#ifdef PGP
int get_fingerprint (parse_info_t *o, char *PGPPATH, pgp_data_t *pdat) {
  int pgp_ok = 1;

  if (pgp_fingerpr (default_trace, PGPPATH, pdat->hex_first->key, pdat)) {
    if (pdat->fingerpr_cnt > 1) {
      error_msg_queue (o, "Too many fingerprints in certificate", ERROR_MSG);
      pgp_ok = 0;
    }
    else
      o->u.kc.fingerpr = strdup (pdat->fp_first->key);
  }
  else {
    error_msg_queue (o, "Couldn't find fingerprint in certificate", ERROR_MSG);
    pgp_ok = 0;
  }

  return pgp_ok;
}

#endif

/* We have parsed a 'certif:' attribute from a 'key-cert' object.
 * Now perform the check to make sure the hexid specified in the
 * 'key-cert:' attribute matches the hexid from the 'certif:'
 * attribute.  We perform this check (and others) by adding the
 * 'certif:' attribute/PGP key block to a temp ring and parsing
 * the output from PGP.  Additionally, we check the following:
 *
 *  1. make sure there is only 1 hexid in the key
 *  2. make sure there is at least 1 owner/uid specified
 *  3. make sure we get a 'Successful' return string from PGP
 *  4. call get_fingerprint () to get the key fingerprint
 *     which checks to make sure there is only 1 fingerprint
 *
 * Return:
 *
 *    file name of key certificate file if there were no errors found
 *      - the file can then be used to easily add the PGP key to the
 *        local ring
 *      - the key fingerprint and owner(s)/uid(s) are saved to be
 *        used for the corresponding 'key-cert' auto-generated fields
 *    NULL otherwise
 */
char *hexid_check (parse_info_t *o) {
#ifdef PGP
  char curline[256], pgpinfn[256], tmp_pgp_dir[256];
  FILE *pgpin;
  char_list_t *p;
  pgp_data_t pdat;
  int pgp_errors = 0, fd;
#endif

  fprintf (dfile, "Enter hexid_check:\n%s\n\n", o->u.kc.certif);

  /* If we don't have PGP installed then there is nothing to do. */
#ifndef PGP
    error_msg_queue (o, "PGP is not installed on the system.  Cannot perform operation.", ERROR_MSG);
    return NULL;
#else

  /* make a tmp directory for the pgp rings */
  umask (0022);
  strcpy (tmp_pgp_dir, "/var/tmp/pgp.XXXXXX");
  mktemp (tmp_pgp_dir);
  if (strlen (tmp_pgp_dir) == 0 ||
      mkdir (tmp_pgp_dir, 00755)) {
    error_msg_queue (o, "Internal error.  Couldn't create temp directory for PGP\n", ERROR_MSG);
    return NULL;
  }

  /* create a file and put the key certificate into it */
  strcpy (pgpinfn, tmpfntmpl);
  fd = mkstemp (pgpinfn);
  if ((pgpin = fdopen (fd, "w")) == NULL) {
    error_msg_queue (o, 
"Internal error.  Could not check 'key-cert:' temp file creation error", ERROR_MSG);
    goto pgp_errors_jump;
  }
  fwrite (o->u.kc.certif, sizeof (char), strlen (o->u.kc.certif), pgpin);
  fclose (pgpin);

  /* add the certificate to a temp ring */
  if (!pgp_add (default_trace, tmp_pgp_dir, pgpinfn, &pdat)) {
    error_msg_queue (o, "Couldn't add key-cert to ring.  Didn't get successful reply from PGP", ERROR_MSG);
    goto pgp_errors_jump;
  }

  /* certificate checks */

  /* do we have more than 1 hex ID? */
  if (pdat.hex_cnt > 1) {
    error_msg_queue (o, "Too many public keys in certificate", ERROR_MSG);
    pgp_errors = 1;
  }
  /* does the hex ID of the 'key-cert' attr match the certificate hex ID ? */
  else if (strcmp (pdat.hex_first->key, o->u.kc.kchexid)) {
    sprintf (curline, "Hex-ID mistmatch: 'key-cert:' (%s)  'certif:' (%s)\n",
	     o->u.kc.kchexid, pdat.hex_first->key);
    error_msg_queue (o, curline, ERROR_MSG);
    pgp_errors = 1;
  }
  /* owner check */
  else if (pdat.owner_first == NULL) {
    error_msg_queue (o, "No uid's/owners found in certificate", ERROR_MSG);
    pgp_errors = 1;
  }
  /* grab all the owners */
  else {
    o->u.kc.owner_count = pdat.owner_cnt;
    for (p = pdat.owner_first; p != NULL; p = p->next)
      o->u.kc.owner = my_concat (o->u.kc.owner, p->key, 0);
  }

  /* get the key fingerprint */
  if (!pgp_errors && 
      !get_fingerprint (o, tmp_pgp_dir, &pdat)) 
    pgp_errors = 1;

  /* debug */
  fprintf (dfile, "exit check_hexid ()\n");
  if (o->u.kc.owner != NULL)
    fprintf (dfile, 
	     "owner count (%d) owner (%s)\n", o->u.kc.owner_count, o->u.kc.owner);
  if (o->u.kc.fingerpr != NULL)
    fprintf (dfile, "fingerpr (%s)\n", o->u.kc.fingerpr);

  /* end debug */

  /* clean up */
  pgp_free (&pdat);

  /* if we have errors then we won't need the key file */
  if (pgp_errors) {
pgp_errors_jump:
    rm_tmpdir (tmp_pgp_dir);  /* can get rid of temporary directory */
    remove (pgpinfn); 
    return NULL;
  }

  rm_tmpdir (tmp_pgp_dir);  /* can get rid of temporary directory */

  /* leave pgp key for possible addition to local ring */
  return strdup (pgpinfn);
#endif
}


/* Given a comma seperated sources is (ie, rx-in: IRROrder(radb,ans,...)),
 * return an error if 'new_source' is already in the list 'sources_list'.
 *
 * Return
 *
 *   0 if new_source is already a member of 'sources_list'
 *     or DB is now defined in irrd.conf file (ie, is now known)
 *   1 otherwise
 *
 */
int irrorder_syntax (parse_info_t *obj, char *sources_list, char *new_source) {
  char buf[MAXLINE], *p;
  /* JW take out extern config_info_t ci */;

  /* See if we know of this DB 
  if (strcasecmp (new_source, "radb")  &&
      strcasecmp (new_source, "ans")   &&
      strcasecmp (new_source, "mci")   &&
      strcasecmp (new_source, "canet") &&
      strcasecmp (new_source, "bell") &&
      strcasecmp (new_source, "ripe")) {
    sprintf (buf, "Unknown database \"%s\"", new_source);
    error_msg_queue (obj, buf, ERROR_MSG);
    return 1;
    }*/

  /* See if we know of this DB 
  if (is_sourcedb (new_source, ci.srcs) == NULL) {
    sprintf (buf, "Unknown database \"%s\"", new_source);
    error_msg_queue (obj, buf, ERROR_MSG);
    return 1;   
  }
  */

  /* Make sure the DB isn't duplicately defined */
  if (sources_list != NULL && strlen (sources_list) < MAXLINE) {
    strcpy (buf, sources_list);
    p = strtok (buf, ",");
    while (p != NULL) {
      if (*p == ' ')
	p++;
      if (!strcmp (p, new_source)) {
	sprintf (buf, "Duplicate database \"%s\"", new_source);
	error_msg_queue (obj, buf, ERROR_MSG);
	return 1;
      }
      p = strtok (NULL, ",");
    }
  }

  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1