/***************************************
  $Revision: 1.15 $

  Example code: Determine which keys to look for.
  
  This is based on the C code that was reversed engineered from existing Perl
  code.  (~ottrey/which_table/which_table.c)

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

#include "isnic.h"
#include "bitmask.h"
#include "memwrap.h"

#define  WK_IMPL
#include "which_keytypes.h"



#define DOMAINNAME "^[ ]*[a-zA-Z0-9--]*(\\.[a-zA-Z0-9--]+)*[ ]*$"
/* add a constraint: there must be at least one character in the domain name
   because the TLD must not be composed of digits only */
#define DOMAINALPHA  "[a-zA-Z]"

#define LEN_MIN 0
#define LEN_MAX 32

#define NETLEN 16
#define NETQUADS 4
#define NETQUAD_MIN 0
#define NETQUAD_MAX 255

#define ASNUM_MIN 1
#define ASNUM_MAX 65535
#define ASNUM_NUMOFFSET 2   /* XXX - (This is really kludgy!) Offset to the number bit of ASNUM */

#define VALIDIP6PREFIX "^[0-9A-F:]*:[0-9A-F:/]*$"     /* at least one colon */
/* "^[0-9A-F]{1,4}(:[0-9A-F]{1,4}){7}$"*/

#define NET "^[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}$"

#define ASNUM "^AS[1-9]+[0-9]*$"

#define ASRANGE "^AS[0-9]+[ ]*([-][ ]*AS[0-9]+){0,1}$"   /* [ ]*(-[ ]*AS[0-9]+)?   */

#define NETNAME "^[A-Z][A-Z0-9-]*$"

#define MAINTAINER "^[A-Z][A-Z0-9-]*$"

#define LIMERICK "^LIM-[A-Z0-9-]+$"

#define KEYCERT "^PGPKEY-[0-9A-F]{8}$"

#define ROUTESETNAME "^RS-[A-Z0-9-_]*$"

#define ASSETNAME "^AS-[A-Z0-9-_]*$"

#define AUTONICPREFIXREGULAR "^AUTO-"

#define IPRANGE "^[0-9]{1,3}(\\.[0-9]{1,3}){0,3}[ ]*-[ ]*[0-9]{1,3}(\\.[0-9]{1,3}){0,3}$"

#define IPADDRESS "^[0-9.]+$"

#define IPPREFIX "^[0-9.]+/[0-9]+$"

#define PEERINGSET "^PRNG-"

#define FILTERSET  "^FLTR-"

#define RTRSET     "^RTRS-"

/*
  XXX This seems to be the same as the Perl code.  But I don't see where a " " is allowed for.
  I.e. Perl -> ^[a-zA-Z][\w\-\.\'\|\`]*$
  Does \w include [ ;:,?/}{()+*#] ?
#define NAME_B "^[a-zA-Z][a-zA-Z_0-9.'|`-]*$"
*/
#define NAME_B "^[a-zA-Z][a-zA-Z_0-9.'|`;:,?/}{()+*#&-]*$"

#define VALIDIP4PREFIX

#define EMAIL "^[.a-zA-Z0-9--]*@[a-zA-Z0-9--]*(\\.[a-zA-Z0-9--]+)*$"

static int perform_regex_test(const char *pattern, char *string) {
  int match;

  char *re;

  re = regcmp(pattern, (char*)0);
  if (regex(re, string) == NULL) {
    match = 0;
  }
  else {
    match = 1;
  }

  free(re); /* not a wrapper, because we have not allocated it */

  return match;
} /* perform_regex_test() */

static int isasnum(char *string) {
  int result='-';
  int as_value;

  /* First check if the string matches an ASNUM */
  result = perform_regex_test(ASNUM, string);

  /* Then check if the value is between ASNUM_MIN and ASNUM_MAX */
  if (result == 1) {
    as_value = atoi(string+ASNUM_NUMOFFSET);
    if ((as_value < ASNUM_MIN) || (as_value > ASNUM_MAX)) {
      /* an invalid value */
      result=0;
    }
  }
  
  return result;
}

/*******************************************************
    # the problem is as follows:
    #
    # we can never find out which NIC handles are possible on the
    # globe since we don't know that they exist
    #
    # we want to solve this with once with DNS :
    #
    # RIPE.registries.int     CNAME whois.ripe.net
    # InterNIC.registries.int CNAME whois.internic.net
    # and so on...

    #
    # 1) it first does a basic syntax check
    #
    #    notes:
    #
    #    - catches InterNIC handles
    #    - catches the JP|JP-JP APNIC exceptions
    #    - limits the number of initials to three with a good reason:
    #      we have a much better chance to find syntax errors like:
    #      RIPE-DK13 and other problems like this
    #
    # 2) checks for valid suffixes
    #    - all 'source:' attribute values from sites that we mirror
    #      are allowed
    #    - country codes are allowed for APNIC compatibility
    #    - APNIC AP|CC-AU exceptions are handled correctly
    #    - -ORG organization InterNIC handles
    #    - -ARIN ARIN handles
    #    - -ORG-ARIN ARIN handles
********************************************************/
static int isnichandle(char *nichdl) {

  char *regexp, *match;
  char ret[1024];
  char *suffix;

  int i;

/* set ret to the empty string */
  ret[0]='\0';

/* 
  # Japanese NIC handles
  #
  # leading zeros in the number part *are* allowed
  #
  # e.g. AB021JP AB199JP-JP
  #
*/
  regexp = regcmp("[A-Z]{2}[0-9]{3}JP(-JP){0,1}",(char *)0);
  match =  regex(regexp,nichdl);
  free(regexp); /* not a wrapper, because we have not allocated it */
  if (match) return 1;

/*
  # Standard NIC handles
  #
  # leading zeros in the number part are *not* allowed
  #
  # InterNIC - TBQ, IP4
  # RIPE format - AB1-RIPE
  # APNIC use two letter country code suffix
  # Austraila have used -1-AU, -2-AU, -CC-AU suffix.
  # Internic used -ORG suffix
  # ARIN use -ARIN suffix
  # ARIN also use -ORG-ARIN suffix
  #
*/
  regexp = regcmp("^[A-Z]{2,4}([1-9][0-9]{0,5}){0,1}((-[^ ]+){0,1})$0$",(char *)0);
  match =  regex(regexp,nichdl,ret);

  free(regexp); /* not a wrapper, because we have not allocated it */

  if (match == NULL) {
    return 0;
  } else {
    if (ret[0] == '\0') {
      return 1;
    } else {
/*   strip leading '-' */
      suffix = ret+1;
/* suffix of local sources */
      for (i=0;i<=NUM_NICPOSTFIX;i++) {
        if ( !strcmp(suffix,nicpostfix[i]) ) {
          return 1;
        }
      }
/* country codes */
      for (i=0;i<NUM_COUNTRIES;i++) {
        if ( !strcmp(suffix,countries[i]) ) {
          return 1;
        }
      }
/* special suffix */
      for (i=0;i<NUM_SPECIAL;i++) {
        if ( !strcmp(suffix,special[i]) ) {
          return 1;
        }
      }
    }
  }
  return 0;
} /* isnichandle() */

static int isdomname(char *string) {
    return (    perform_regex_test(DOMAINNAME, string)
	     && perform_regex_test(DOMAINALPHA, string));
}

/*
 I split the isname up into isname_a & isname_b.  And created isname_ab to join them together.
  - So I can test it properly.  -ottrey
 */
static int isname_a(char *string) {
    return perform_regex_test(AUTONICPREFIXREGULAR, string);
}

static int isname_b(char *string) {
    return perform_regex_test(NAME_B, string);
}

static int isname_ab(char *string) {
    return (isname_a(string) || isname_b(string));
}

static int isnetname(char *string) {
    return perform_regex_test(NETNAME, string);
} /* wk_is_netname() */

static int wk_is_name(char *key) {
    /* Everything matches to name */
    return 1;
} /* wk_is_name() */

static int wk_is_nic_hdl(char *key) {
    return isnichandle(key);
} /* wk_is_nic_hdl() */

static int wk_is_email(char *key) {
    return perform_regex_test(EMAIL, key);
} /* wk_is_email() */

static int wk_is_mntner(char *key) {
    return perform_regex_test(MAINTAINER, key); 
} /* wk_is_mntner() */

static int wk_is_key_cert(char *key) {
    return perform_regex_test(KEYCERT, key);
} /* wk_is_key_cert() */

static int wk_is_ipaddress(char *key) {
    return perform_regex_test(IPADDRESS, key);
} /* wk_is_key_cert() */

static int wk_is_iprange(char *key) {
    return perform_regex_test(IPRANGE, key);
} /* wk_is_iprange() */

static int wk_is_ipprefix(char *key) {
    return perform_regex_test(IPPREFIX, key);
} /* wk_is_iprange() */

static int wk_is_ip6prefix(char *key) {
    return perform_regex_test(VALIDIP6PREFIX, key);
} /* wk_is_ip6prefix() */

static int wk_is_netname(char *key) {
    return isnetname(key);
} /* wk_is_netname() */

/* XXX Note: This function uses the same call as wk_is_netname(). */
static int wk_is_net6name(char *key) {
    return isnetname(key);
} /* wk_is_netname() */

static int wk_is_autnum(char *key) {
    return isasnum(key);
} /* wk_is_autnum() */

static int wk_is_asrange(char *key) {
    return perform_regex_test(ASRANGE, key);
} /* wk_is_autnum() */

static int wk_is_assetname(char *key) {
    return perform_regex_test(ASSETNAME, key);
} /* wk_is_assetname() */

static int wk_is_routesetname(char *key) {
    return perform_regex_test(ROUTESETNAME, key);
} /* wk_is_routesetname() */

static int wk_is_domain(char *key) {
    return isdomname(key);
} /* wk_is_domname() */

static int wk_is_hostname(char *key) {
  /* XXX Why is there a hostname & a domainname? */
  /* Answer - hostname can be a domainname or an IP */
    return (isdomname(key) || wk_is_iprange(key));
} /* wk_is_hostname() */

static int wk_is_limerick(char *key) {
    return perform_regex_test(LIMERICK, key);
} /* wk_is_limerick() */

static int wk_is_peeringset(char *key) {
    return perform_regex_test(PEERINGSET, key);
} /* wk_is_peeringset() */

static int wk_is_rtrset(char *key) {
    return perform_regex_test(RTRSET, key);
} /* wk_is_rtrset() */

static int wk_is_filterset(char *key) {
    return perform_regex_test(FILTERSET, key);
} /* wk_is_filterset() */

/* WK_to_string() */
/*++++++++++++++++++++++++++++++++++++++
  Convert the which keytypes bitmap into a string.

  mask_t wk The which keytypes mask to be converted.

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

  ++++++++++++++++++++++++++++++++++++++*/
char *WK_to_string(mask_t wk) {

  return MA_to_string(wk, Keytypes);

} /* WK_to_string() */

/* WK_new() */
/*++++++++++++++++++++++++++++++++++++++
  Create a new which keytypes bitmap.

  char *key The key to be examined.

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

  ++++++++++++++++++++++++++++++++++++++*/
mask_t WK_new(char *key) {
  mask_t wk; 

  wk = MA_new(MA_END);

  MA_set(&wk, WK_NAME,         wk_is_name(key));
  MA_set(&wk, WK_NIC_HDL,      wk_is_nic_hdl(key));
  MA_set(&wk, WK_EMAIL,        wk_is_email(key));
  MA_set(&wk, WK_MNTNER,       wk_is_mntner(key));
  MA_set(&wk, WK_KEY_CERT,     wk_is_key_cert(key));
  MA_set(&wk, WK_IPADDRESS,    wk_is_ipaddress(key));
  MA_set(&wk, WK_IPRANGE,      wk_is_iprange(key));
  MA_set(&wk, WK_IPPREFIX,     wk_is_ipprefix(key));
  MA_set(&wk, WK_IP6PREFIX,    wk_is_ip6prefix(key));
  MA_set(&wk, WK_NETNAME,      wk_is_netname(key));
  MA_set(&wk, WK_NET6NAME,     wk_is_net6name(key));
  MA_set(&wk, WK_AUTNUM,       wk_is_autnum(key));
  MA_set(&wk, WK_ASSETNAME,    wk_is_assetname(key));
  MA_set(&wk, WK_ROUTESETNAME, wk_is_routesetname(key));
  MA_set(&wk, WK_DOMAIN,       wk_is_domain(key));
  MA_set(&wk, WK_HOSTNAME,     wk_is_hostname(key));
  MA_set(&wk, WK_LIMERICK,     wk_is_limerick(key));
  MA_set(&wk, WK_ASRANGE,      wk_is_asrange(key));
  MA_set(&wk, WK_PEERINGSET,   wk_is_peeringset(key));
  MA_set(&wk, WK_FILTERSET,    wk_is_filterset(key));
  MA_set(&wk, WK_RTRSET,       wk_is_rtrset(key));
  
  return wk;

} /* WK_new() */
