#
#	$RCSfile: syntax.pl,v $
#	$Revision: 0.55 $
#	$Author: ripe-dbm $
#	$Date: 1995/02/10 13:53:46 $
#
#       ARGUMENTS:      *ASSOC object
#       RETURNS:        INTEGER object_status
#
#       Object status = $O_OK, $O_WARNING, $O_ERROR
#       Object will be changed to have warnings and errors
#
# This is the really ugly bit, where the syntax of all the fields is checked
# This is completely independent of the config file and needs additions if
# you add your own fields. It does not check whether fields are allowed in
# this object, whether they are supposed to be multiple or any of that
# That part is basic configuration driven and can be found in enparse.pl
#
# The syntax stuff needs to be re-written at a later date to allow for
# configurable syntax. This is in the dreams of someones mind.....
#

require "adderror.pl";
require "net2net.pl";    # various routines to make classless life easier
require "misc.pl";	 # this one has quite a few used sub routines
require "maintainer.pl";	# For mnt-by verification
require "enukey.pl";		# for nic handle

# This routine checks whether a specific object with a certain source
# and type exists. It will return the number of hits. Does NOT work for
# IP address just yet and has to be moved away from here .... If $lookup
# is set, then it will also return the first object in the list.

require "dbopen.pl";
require "dbmatch.pl";
require "dbclose.pl";
require "enread.pl";

sub ExistsObject {
    local($key, $type, $source, $lookup) = @_;

    local(*i) ='curdb';
    local($dbfile) = "";
    local(%nothing) = ();
    local(@keys) =($key);
    local(@matches);
    local(%en) = ();

    if ($TYPE{$source} eq "SPLIT") {
	$dbfile = "$DBFILE{$source}.$type";
    } else {
	$dbfile = $DBFILE{$source};
    }

    if (&dbopen(i, *nothing, 0, $dbfile)) {
	@matches = &dbmatch(*i, *keys, 1);
    } else {
	&dbclose(*i);
	return -1;
    }
    if ($lookup) {
	%en = &enread(i, $matches[0]);
    }

    &dbclose(*i);

    if ($lookup) {
	return $#matches+1, %en;
    } else {
	return $#matches+1;
    }
}


sub checksyntax {

    local(*object) = @_;
    local($rtcode) =$O_OK;
    local($itmp, $val, $msg);

    print STDERR "checksyntax - called\n" if $opt_V;

    foreach $itmp (keys %object) {
	if ($object{$itmp} eq "") {
	    ($val, $msg) = &dosyntax($itmp, "", *object);
	    if ($val == $O_WARNING) {
		&addwarning(*object, $msg);
		$rtcode = $O_WARNING if $rtcode == $O_OK;
	    }
	    elsif ($val == $O_ERROR) {
		&adderror(*object, $msg);
		$rtcode = $O_ERROR;
	    }
	} else {
#
# Got to preprocess the multi-line semantic attributes. sigh.. Did I really
# think this was a good idea ??
# The way this works is $peer and $wt (this is a combination depending on the
# attribute) are used as a key to check wrapped lines. 
# This is probably not the best way of doing this as you to do a lot of 
# splitting to get the correct unique keys.
# You also have to split differently depending on whether syntax sugar exists.
#
	    if($itmp eq "ai" || $itmp eq "ao" || $itmp eq "it" || $itmp eq "io") {
		local($FLAG) = $itmp;
		local(@array) = split(/\n/, $object{$itmp});
		local($j,$k) = 0;
		local(%linewrap) = ();
		local(%newval) = ();
		foreach $j (0..$#array) {
#
# as-in lines
#
		    if($FLAG eq "ai") {
			if($array[$j] =~ /^from/) {
			    ($sugar1, $peer, $wt, $sugar2, $pol) =
				split(/\s+/, $array[$j], 5);
			    if($sugar2 ne "accept") {
				&adderror(*object, "keyword problem in as-in line for peer $peer cost $wt");
				$rtcode = $O_ERROR;
                                next;
			    }
			} else {
			    ($peer, $wt, $pol) = split(/\s+/, $array[$j], 3);
			}
			$object{$itmp} =~ s/from\s+|accept\s+//g;
#
# as-out lines
#
		    } elsif ($FLAG eq "ao") {
			if($array[$j] =~ /^to/) {
			    ($sugar1, $peer, $sugar2, $pol) = 
				split(/\s+/, $array[$j], 4);
			    $wt = 1;
			    if($sugar2 ne "announce") {
				&adderror(*object, "keyword problem in as-out line for peer $peer");
				$rtcode = $O_ERROR;
				next;
			    }
			} else {
			    ($peer, $pol) = split(/\s+/, $array[$j], 2);
			    $wt = 1;
			}
			$object{$itmp} =~ s/to\s+|announce\s+//g;
#
# interas-in lines
#
		    } elsif ($FLAG eq "it") {
#
# Get rid of spaces in (<pref-type>=<value>)
#
			$array[$j] =~ 
			    s/\(\s*pref\s*\=\s*(\S+)\s*\)/\(pref=\1\)/;
			if($array[$j] =~ /^from/) {
			    ($sugar1, $peer, $lid, $rid, $cost, $sugar2, $pol) 
				= split(/\s+/, $array[$j], 7);
			    if($sugar2 ne "accept") {
				&adderror(*object, "keyword problem in interas-in line for peer $peer cost $cost");
				$rtcode = $O_ERROR;
				next;
			    }
			    $wt = "$lid-$rid-$cost";
			} else {
			    ($peer, $lid, $rid, $cost, $pol)  = 
				split(/\s+/, $array[$j], 5);
			    $wt = "$lid-$rid-$cost";
			}
			$object{$itmp} =~ s/from\s+|accept\s+//g;
#
# interas-out lines
#
		    } elsif ($FLAG eq "io") {
			local($gotmet) = 0;
#
# This is where you have insert new ``mertic-type'' values and get rid of 
# spaces
#
			if ($array[$j] =~ /metric-out/) {
			    $array[$j] =~ s/\(\s*metric\-out\s*\=\s*(\S+)\s*\)/\(metric-out=\1\)/;
			    $gotmet = 1;
			}	
			if($array[$j] =~ /^to/) {
			    if($gotmet) {
				($sugar1, $peer, 
				 $lid, $rid, $metric, $sugar2, $pol) = 
				     split(/\s+/, $array[$j], 7);
				$wt = "$lid-$rid-$metric";
			    } else {
				($sugar1, $peer, $lid, $rid, $sugar2, $pol) =
				    split(/\s+/, $array[$j], 6);
				$wt = "$lid-$rid";
			    }
			    if($sugar2 ne "announce") {
				&adderror(*object, "keyword problem in interas-out line for peer $peer");
				$rtcode = $O_ERROR;
				next;
			    }
			} else {
			    if($gotmet) {
				($peer, $lid, $rid, $metric, $pol) =
				    split(/\s+/, $array[$j], 5);
				$wt = "$lid-$rid-$metric";
			    } else {
				($peer, $lid, $rid, $pol) = 
				   split(/\s+/, $array[$j], 4);
				$wt = "$lid-$rid";
			    }
			}
			$object{$itmp} =~ s/to\s+|announce\s+//g;
		    }
#
# Now finally check if the lines are the same.
#
		    if($newval{"$peer:$wt"}) {
			if($linewrap{"$peer:$wt"}) {
			    $newval{"$peer:$wt"} = $newval{"$peer:$wt"}." ".$pol;
			} else {
			    $newval{"$peer:$wt"} = $newval{"$peer:$wt"}."\n".$array[$j];
			}
		    } else {
			$newval{"$peer:$wt"} = $array[$j];
		    }
		    $linewrap{"$peer:$wt"} = 1;
		}
#
# Now loop through the value and syntax check the re-built line
#	   
		foreach $k (keys %newval) {
		    foreach $l (split(/\n/, $newval{$k})) {
			local($val, $msg) = &dosyntax("$FLAG", $l, *object);
			if ($val == $O_WARNING) {
			    &addwarning(*object, $msg);
			    $rtcode = $O_WARNING if $rtcode == $O_OK;
			}
			elsif ($val == $O_ERROR) {
			    &adderror(*object, $msg);
			    $rtcode = $O_ERROR;
			}
		    }
		}
#
# Otherwise just split on newlines and pass line by line to syntax checker
#
	    } else {
		foreach $j (split(/\n/, $object{$itmp})) {
		    local($val, $msg) = &dosyntax($itmp, $j, *object);
		    if ($val == $O_WARNING) {
			&addwarning(*object, $msg);
			$rtcode = $O_WARNING if $rtcode == $O_OK;
		    }
		    elsif ($val == $O_ERROR) {
			&adderror(*object, $msg);
			$rtcode = $O_ERROR;
		    }
		}
	    }
	}
    }
    print STDERR "checksyntax - returned\n" if $opt_V;
    return $rtcode;
}

sub dosyntax {

    local($key, $value, *object) = @_;

#
# THE FIRST SET OF ATTRIBUTES MAY NOT HAVE AN EMPTY VALUE IF THEY EXIST
#

#
# ua - authorise
#
    if ($key eq "ua") {
        if ($value !~ /\S/) {
            return $O_ERROR, "illegal authorisation value";
        }
        return;
    }

#
# uo - override
#

    if ($key eq "uo") {
        if ($value !~ /\S/) {
            return $O_ERROR, "illegal override value";
        }
    }

#
# ud - delete
#
# The delete is a bit of a pain. Since we want to be able to delete
# objects that actually contain syntax errors, they are NOT syntax
# checked. Therefore, all the syntax checking for deletes is actually
# done in misc.pl sub &hasdelete. This is not very nice, but the only
# thing that actually works. Below is commented out.

#    if ($key eq "ud") {
#        if ($value !~ /\S/) {
#            return $O_ERROR, "delete attribute must contact email address and reason for delete";
#        }
#        return;
#    }

#
#
# AFTER THIS, ATTRIBUTES THAT ARE DEFINES BUT EMPTY ARE OK
#

    return if $value eq "";
#
# aa - see na
#

#
# ac - admin-c
#
    if ($key eq "ac") {
	if (!&isname($value)) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# ad - address
#   
    if ($key eq "ad") {
	if ($value !~ /^.*$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# ae - as-exclude
#
    if ($key eq "ae") {
	local($sugar1, $as, $sugar2, $rest) = "";
	if($value =~ /^exclude/) {
	    ($sugar1, $as, $sugar2, $rest) = split(/\s+/, $value, 4);
	    if($sugar2 ne "to") {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	    }
	} else {
	    ($as, $rest) = split(/\s+/, $value,2);
	}
	if(!&isasnum($as)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\" - neigbor peer".
		" $as doesn't look like an AS";
	}
	if(&isasnum($rest) || 
	   &iscommunity($rest) || 
	   &isasmacro($rest) ||
	   ($rest eq "ANY")) {
	    $object{$key} =~ s/exclude\s+|to\s+//g;
	} else {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\" - exclude-route-keyword".
		" $rest unknown";
	}
	return;
    }
#
# ai - as-in
#   
    if ($key eq "ai") {
# 
# This line has been pre-processed above.
# remove syntax fluff, flip to unpper case for ases and remove leading WS
#
	$value =~ s/from\s*//;
	$value =~ s/accept\s*//;
	$value =~ s/[aA][sS]/AS/g;
	$value =~ s/^\s+//;
#
# split the line up into AS, cost and the policy
#
	local($as,$pref,$pol) = split(/\s+/,$value,3);
	if (!&isasnum($as)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\"\nneigbour peer".
		" $as doesn't look like an AS";
	}
	if (!$pref) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\"\nyou must give a cost";
	}
	if (!&isaspref($pref)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\"\ncost $pref ".
		"must be a positive integer";
	}
	if (!$pol) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\"\n\tno ".
		"routing policy expression given";
	}
#
# now check equal brackets and braces
#
	if(!&isbracket($pol)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: peer $as cost $pref\"" .
		"\n\tunequal brackets \"\(\)\"\n";
	}
	if(!&isbrace($pol)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: peer $as cost $pref\"" .
		"\n\tunequal braces \"\{\}\"\n";
	}
#
# Now grab the netlist entries and check they are ok
#
	local($tmppol) = $pol;
	while($tmppol =~ s/(\{[^\}]*\})// ) {
	    if(!&isnetlist($1)) {
		return $O_ERROR, 
		"syntax error in \"$ATTL{$key}: peer $as cost $pref\"".
		    "\n\tnetlist error $1";
	    }
	}
#
# Now check the actual keywords
#
	while($tmppol =~ s/(\S+)//) {
	    if (!&isaskeyword($1)) {
		return $O_ERROR, 
		"syntax error in \"$ATTL{$key}: peer $as cost $pref\"\n\t$1 ".

		    "is not a routing policy KEYWORD";
	    }
	}
	return;
    }
#
# al - as-list
#
    if ($key eq "al") {
	$value =~ tr/a-z/A-Z/;
	local(@aslist) = split(/\s+/, $value);
	local($i);
	foreach $i (@aslist) {
	    if(!&isasnum($i) && !&isasmacro($i)) {
		return $O_ERROR, "illegal value \"$i\" in \"$ATTL{$key}\"";
	    }
	}
	return;
    }
#
# an - aut-num
#    
    if ($key eq "an") {
	$value =~ tr/a-z/A-Z/;
	if (!&isasnum($value)) {
	    return $O_ERROR,
	    "syntax error in \"$ATTL{$key}\" - $value is not a valid AS";
	}
	if (($object{$key} =~ tr/a-z/A-Z/)) {
	    return $O_WARNING, "\"$ATTL{$key}\" value uppercased";
	}
	return;
    }
    
#
# am - as-macro
#
    if ($key eq "am") {
	if(!&isasmacro($value)) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# ao - as-out
#
    if ($key eq "ao") {
	$value =~ s/to//;
	$value =~ s/announce//;
	$value =~ s/[aA][sS]/AS/g;
	$value =~ s/^\s+//;
#
# split up into AS and policy
#
	local($as,$pol) = split(/\s+/,$value,2);
	if (!&isasnum($as)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\" - ".
		"neigbour peer $as doesn't look like an AS";
	}
	if (!$pol) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\" - ".
		"no routing policy expression given";
	}
#
# now check equal brackets and braces.
#
	if(!&isbracket($pol)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: peer $as \"" .
		"\n\tunequal brackets \"\(\)\"\n";
	}
	if(!&isbrace($pol)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: peer $as \"" .
		"\n\tunequal brackets \"\(\)\"\n";
	}
#
# Now grab loop through netlist entries and check they are ok
# Here a netlist entry is anything between braces.
#
	local($tmppol) = $pol;
	while($tmppol =~ s/(\{[^\}]*\})// ) {
	    if(!&isnetlist($1)) {
		return $O_ERROR, 
		"syntax error in \"$ATTL{$key}: peer $as \"".
		    "\n\tnetlist error $1";
	    }
	}
	while($tmppol =~ s/(\S+)//) {
	    if (!&isaskeyword($1)) {
		return $O_ERROR, 
		"syntax error in \"$ATTL{$key}: peer $as \"".
		    "\n\t$1 is not a routing policy KEYWORD";
	    }
	}

    }
#
# as - aut-sys
#   
    if ($key eq "as") {
	if ($value !~ /^\d+$/) {
	    if (!&isasnum($value)) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	    }
	}
	return;
    }
#
# at - auth
#
    if ($key eq "at") {
	local(@authstr) = split(/\s+/, $value, 2);
	if ($authstr[0] eq "NONE") {
	    if ($authstr[1] !~ /^$/) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\"".
		    "- $authstr[1] is extraneous for $authstr[0]";
	    } else {
	     	return;
	    }
	}
	if ($authstr[0] eq "CRYPT-PW") {
	    if(length($authstr[1]) != 13) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\"" .
		    " - password \"$authstr[1]\" is incorrect length";
	    } else {
		return;
	    }
	}	
	elsif ($authstr[0] eq "MAIL-FROM") {
	    local($regex) = $value;
	    $regex =~ s/^\s*MAIL\-FROM\s*//;
	    eval "/$regex/;";
	    if ($@) {
		return $O_ERROR, "\"$regex\" is not a legal regular expression";
	    } else {
		return;
	    }
	}
	    
	else {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\" $value";
	}
	return;
    }
#
# au - authority
#
    if ($key eq "au") {
	if ($value !~ /^[a-zA-Z0-9\-\.\ \,\(\)\/]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# av - advisory
#
    if ($key eq "av") {
	local(@list) = split(/\s+/, $value);
        if (!&isasnum($list[0])) {
            return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - $list[0] is no a valid peer";
        }
	return;
    }
#
# the RIPE-60 tags are just given a simple parse - not really needed
# as they are basically guarded.
#
# bg - bdry-gw
#
    if ($key eq "bg") {
	if ($value !~ /^[A-Z0-9\-]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# bi - bis
#      bis - Boundary intermediate system i.e. CLNS nonsense
#
    if ($key eq "bi") {
	local(@prefixes) = split(/\s+/, $value);
	local($i);
	if ($#prefixes > 1) {
	    return $O_ERROR, "too many prefixes in \"$ATTL{$key}\"";
	}
	foreach $i (@prefixes) {
	    if (!&isclnsprefix($i)) {
		return $O_ERROR, 
		"illegal NSAP prefix syntax in \"$ATTL{$key}\"";
	    }
	}
	return;
    }
# 
# bl - bdrygw-l
#
    if ($key eq "bl") {
	if ($value !~ /^[A-Z0-9\-\ ]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	
	return;
    }
#
# Try to do something clever with the changed field
#
# ch - changed
#
    if ($key eq "ch") {
	local($i);
        (@tmp) = split(/\s+/, $value);
	$email = "$tmp[0]";
	foreach $i (1..$#tmp-1) { # This is for emails with spaces ;-(
	    $email .= " $tmp[$i]";
	}
	$date = "$tmp[$#tmp]" if ($tmp[$#tmp] =~ /^\d+$/);
	if (!(&isemail($email))) {
	    return $O_ERROR, "syntax error in e-mail part of \"$ATTL{$key}\"";
	}
	local($s, $m, $h, $md, $mo, $y) = localtime(time);
	$mo += 1;
	$md = "0".$md unless $md > 9;
	$mo = "0".$mo unless $mo > 9;
	$y = "0".$y unless $y > 9;
	local($curdate) = "$y$mo$md";

	if ($date eq "") {
	    # $object{$key} .= " $curdate";
	    # return $O_WARNING, 
	    # "todays date ($curdate) added to \"$ATTL{$key}\" attribute";
	    return $O_ERROR, 
	    "No date specified in \"$ATTL{$key}\" attribute";
        }
	if ($date !~ /^(\d\d)(\d\d)(\d\d)$/) {
	    return $O_ERROR, "date part of \"$ATTL{$key}\"".
		"not in YYMMDD format";
	}
	# 1988 is the start of the world. This is where we test for proper
	# date values of YYMMDD

	if (($1 < 88) || ($2 > 12) || ($3 > 31)) {
	    return $O_ERROR, "date part of \"$ATTL{$key}\" is not a valid YYMMDD value";
	}
	
	if ($date gt $curdate) {
	    $object{$key} =~ s/$date/$curdate/;
	    return $O_WARNING,
	    "date in \"$ATTL{$key}\" ($date) is in the ".
		"future - changed to $curdate";
	}
	return;
    }
#
# This is the "community" stuff.
# It needs to make sure RIPE-81 keywords aren't there.
#
# cl - comm-list
#
    if ($key eq "cl") {
	local(@crap) = split(/\s+/,$value);
	foreach $j (@crap) {
	    if (!&iscommunity($j)) {
		return $O_ERROR, 
		"syntax error in \"$ATTL{$key}\" - ".
		    "is an illegal keyword community name \"$j\"";
	    }
	    local($copy) = $j;
	    $copy =~ tr/A-Z/a-z/;
	    local($stat) = &ExistsObject($copy, "cm", $object{"so"});
	    if ($stat < 1 ) {
		return $O_ERROR, "community \"$j\" does not exist in ".
		    "the $object{\"so\"} database";
	    }
	} 
	return;
    }
#
# cm - community
#
    if ($key eq "cm") {
	if (!&iscommunity($value)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - ".
		"illegal community name \"$j\"";
	} 
	return;
    }
    
#
# co - connect
#
# check the list of connect values from the config.
#
    if ($key eq "co") {
	foreach $j (split(/\s+/, $value)) {
	    if (!$CONNECT{$j}) {
		return $O_ERROR, "unknown connect value \"$j\"";
	    }
	}
	return;
    }
#
# cy - country
#
    if ($key eq "cy") {
	if (!$COUNTRY{$value}) {
	    return $O_ERROR, "unknown country \"$value\"";
	} else {
	    if ($COUNTRY{$value} ne $value) {
		$object{$key} = $COUNTRY{$value};
		return $O_WARNING, 
		"country \"$value\" changed to \"$COUNTRY{$value}\"";
	    }
	}
	return;
    }
#
# de - descr
#
    if ($key eq "de") {
	if ($value !~ /^.*$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# df - default
#
    if ($key eq "df") {
	local($rest) = "";
	if ($object{"dp"}) {
	    $value =~ tr/A-Z/a-z/;
	    ($prefix, $pref,$rest) = split(/\s+/, $value, 3);
	    if (!&isclnsprefix($prefix)) {
		return $O_ERROR, 
		"syntax error in \"$ATTL{$key}: $value\" - incorrect ".
		    "NSAP prefix";
	    }
	}
	else {
	    $value =~ tr/a-z/A-Z/;
	    ($as,$pref,$rest) = split(/\s+/,$value, 3);
	    if (!&isasnum($as)) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}: $value\" ".
		    "- default peer $as doesn't look like an AS";
	    }
	}
	if (!$pref) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\" - you must give a cost";
	}
	if (!&isaspref($pref)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}: $value\" - ".
		"cost $pref must be a positive integer";
	}
	if($rest && !$object{"dp"}) {
	    $rest =~ s/STATIC/static/;
	    $rest =~ s/DEFAULT/default/;
	    if (&isnetlist($rest) ||
		($rest eq "static") || 
		($rest eq "default")) {}
	    else {
		return $O_ERROR,
		"syntax error in \"$ATTL{$key}\" - ".
		    "\"$rest\" is invalid";
	    }
	    $object{$key} =~ s/STATIC/static/;
	    $object{$key} =~ s/DEFAULT/default/;
	    $object{$key} =~ s/[aA][sS]/AS/g;
	}
	return;
    }
#
# Check to make sure the network list looks reasonable
#
#
# di - dom-net
#
    if ($key eq "di") {
	local(@list) = split(/\s+/,$value);
	local($j) = 0;
	foreach $j (0..$#list) {
	    if (!&isnetnum($list[$j])) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\" - ".
		    "illegal IP network number $list[$j]";
	    }
	}
	return;
    }
#
# dm - dom-in
#
    if($key eq "dm") {
	local($bis,$pref,@crap) = split(/\s+/,$value);
        if (!&isclnsprefix($bis)) {
            return $O_ERROR, 
            "syntax error in \"$ATTL{$key}: $value\" - dom-prefix".
                " $bis doesn't look like an NSAP";
        }
        if (!$pref) {
            return $O_ERROR, 
            "syntax error in \"$ATTL{$key}: $value\" - you must give a cost";
        }
        if (!&isaspref($pref)) {
            return $O_ERROR, 
            "syntax error in \"$ATTL{$key}: $value\" - cost $pref ".
                "must be a positive integer";
        }
        if ($#crap < 0 ) {
            return $O_ERROR, 
            "syntax error in \"$ATTL{$key}: $value\" - no ".
                "routing policy expression given";
        }
        foreach $k (@crap) {
            if (!&isclnskeyword($k)) {
                return $O_ERROR, 
                "syntax error in \"$ATTL{$key}: $value\" - $k ".
                    "is not a routing policy KEYWORD";
            }
        }
        return;
    }
#
# dn - domain
#
    if ($key eq "dn") {
	if (!&isdomname($value)) {
	    return $O_ERROR, "illegal domain name in $value";
	}
	return;
    }
#
# do - dom-out
#
    if ($key eq "do") {
	local($bis,@crap) = split(/\s+/,$value);
        if (!&isclnsprefix($bis)) {
            return $O_ERROR, 
            "syntax error in \"$ATTL{$key}: $value\" - ".
                "dom-prefix \"$bis\" doesn't look like an NSAP prefix";
        }
        if ($#crap < 0 ) {
            return $O_ERROR, 
            "syntax error in \"$ATTL{$key}: $value\" - ".
                "no routing policy expression given";
        }
        foreach $k (@crap) {
            if (!&isclnskeyword($k)) {
                return $O_ERROR, "syntax error in \"$ATTL{$key}: $value\" - ".
                    "$k is not a routing policy KEYWORD";
            }
        }
        return;
    }
#
# dp - dom-prefix
#
    if ($key eq "dp") {
	if (!&isclnsprefix($value)) {
	    return $O_ERROR, "illegal NSAP prefix format in \"$ATTL{$key}\"";
	}
	return;
    }
#
# da - dom-name
#
    if ($key eq "da") {
	if ($value !~ /^[a-zA-Z\-0-9\.]+$/) {
	    return $O_ERROR, "illegal $ATTL{$key} name";
	}
	return;
    }
#
# dt - upd-to
#
    if ($key eq "dt") {
	if (!&isemail($value)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" ".
		"- \"$value\" is not in \(RFC822\) format";
	}
	return;
    }
#
# em - e-mail
#
    if ($key eq "em") {
	if (!&isemail($value)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" ".
		"- \"$value\" is not in \(RFC822\) format";
	}
	return;
    }
#
# gd - guardian
#
    if ($key eq "gd") {
	if (!(&isemail($value))) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - ".
		"guardian must be a mailbox entry";
	}
	return;
    }

#
# gw - gateway
#
    if ($key eq "gw") {
	if ($value !~ /^[a-zA-Z0-9\-\.\ ]+$/) {
	    return $O_WARNING, "syntax error in \"$ATTL{$key}\"";	
	}
	return;
    }
#
# ho - hole
#
# still need to check against route entry
#
    if ($key eq "ho") {
	local($stat, $msg, @str) = &netpre_verify($value);
	if($stat == $NOK) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"\n$msg\n";
	}		
	return;
    }
#
# if - ifaddr
#
    if ($key eq "if") {
	local($if, $mask) = split(/\s+/, $value, 2);
	if(!&isipaddr($if)) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\""
		. " $if is incorrect";
	}
	if(!&ismask($mask)) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\""
		. " $mask is incorrect";
	}
	return;
    }
#
# ii - ias-int
#

    if ($key eq "ii") {
	local(@iistr) = split(/\s+/,$value);	
	if ($#iistr != 1 ) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - wrong number of components";
	}
	if (!&isipaddr($iistr[0])) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - \"$iistr[0]\" ".
		"is not a valid IP address";
	}
	if (!&isasnum($iistr[1])) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - ".
		"\"$iistr[1]\" is not a valid AS";
	}
	return;
    }
#
# it - interas-in
#
    if ($key eq "it") {
#
# This line has been pre-processed above.
# remove syntax fluff, flip to unpper case for ases and remove leading WS
#
        $value =~ s/from\s*//;
        $value =~ s/accept\s*//;
        $value =~ s/[aA][sS]/AS/g;
        $value =~ s/^\s+//;
#
# split the line up into AS, lid, rid, cost and the policy
#
	local($as, $lid, $rid, $pref, $pol) = split(/\s+/,$value, 5);
	if (!&isasnum($as)) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: $value\"\n\tneigbour peer".
                " $as doesn't look like an AS";
        }
	if (!&isipaddr($lid) || !&isipaddr($rid)) {
	    return $O_ERROR,
            "syntax error in \"$ATTL{$key}: $value\"\n\tip address error";
	}
	if (!$pref) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: $value\"\nyou must give a cost";
        }
        if ($pref !~ /^\(pref=(\S+)\)$/) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: $value\" preferece is invalid";
	}
	if ($1 ne "MED" && $1 !~ /^\d+$/) {
	    return $O_ERROR,
                "syntax error in \"$ATTL{$key}: $value\"".
		    "\n\t<pref-type> value \"$1\" is invalid";
	}
        if (!$pol) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: $value\"\n\tno ".
                "routing policy expression given";
        }
#
# now check equal brackets and braces
#
        if(!&isbracket($pol)) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: peer $as cost $pref\"" .
                "\n\tunequal brackets \"\(\)\"\n";
        }
        if(!&isbrace($pol)) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: peer $as cost $pref\"" .
                "\n\tunequal braces \"\{\}\"\n";
        }
#
# Now grab the netlist entries and check they are ok
#
        local($tmppol) = $pol;
        while($tmppol =~ s/(\{[^\}]*\})// ) {
            if(!&isnetlist($1)) {
                return $O_ERROR,
                "syntax error in \"$ATTL{$key}: peer $as cost $pref\"".
                    "\n\tnetlist error $1";
            }
        }
#
# Now check the actual keywords
#
        while($tmppol =~ s/(\S+)//) {
            if (!&isaskeyword($1)) {
                return $O_ERROR,
                "syntax error in \"$ATTL{$key}: peer $as cost $pref\"\n\t$1 ".
                    "is not a routing policy KEYWORD";
            }
        }
	return;
    }
#
# io - interas-out
#
    if ($key eq "io") {
	local($gotmet) = 0;
	local($as, $lib, $rid, $metric, $pol);
        $value =~ s/to//;
        $value =~ s/announce//;
        $value =~ s/[aA][sS]/AS/g;
        $value =~ s/^\s+//;
#
# split up into parts
#
	if ($value =~ /metric-out/) {
	    $gotmet = 1;
	    ($as, $lid, $rid, $metric, $pol) = split(/\s+/, $value, 5);
	} else {
	    ($as, $lid, $rid, $pol) = split(/\s+/, $value, 4);
	}
        if (!&isasnum($as)) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: $value\" - ".
                "neigbour peer $as doesn't look like an AS";
        }
        if (!&isipaddr($lid) || !&isipaddr($rid)) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: $value\"\n\tip address error";
        }
	if ($gotmet) {
	    if ($metric !~ /^\(metric-out=(\S+)\)$/) {
		return $O_ERROR,
		"syntax error in \"$ATTL{$key}: $value\"".
		    "\n\t<metric-type> is invalid";
	    }
	    if ($1 ne "IGP" && $1 !~ /^\d+$/) {
		return $O_ERROR, 
		"syntax error in \"$ATTL{$key}: $value\"".
		    "\n\t<metric-type> value \"$1\" is invalid";
	    }
	}
        if (!$pol) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: $value\" - ".
                "no routing policy expression given";
        }
#
# now check equal brackets and braces
#
        if(!&isbracket($pol)) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: peer $as \"" .
                "\n\tunequal brackets \"\(\)\"\n";
        }
        if(!&isbrace($pol)) {
            return $O_ERROR,
            "syntax error in \"$ATTL{$key}: peer $as \"" .
                "\n\tunequal brackets \"\(\)\"\n";
        }
#
# Now grab the netlist entries and check they are ok
#
        local($tmppol) = $pol;
        while($tmppol =~ s/(\{[^\}]*\})// ) {
            if(!&isnetlist($1)) {
                return $O_ERROR,
                "syntax error in \"$ATTL{$key}: peer $as \"".
                    "\n\tnetlist error $1";
            }
        }
        while($tmppol =~ s/(\S+)//) {
            if (!&isaskeyword($1)) {
                return $O_ERROR,
                "syntax error in \"$ATTL{$key}: peer $as \"".
                    "\n\t$1 is not a routing policy KEYWORD";
            }
        }
        $object{$key} =~ s/[aA][sS]/AS/g;
	return;
    }
#
# This is simple for now. TB
# Will change the isnetnum routine to return various codes and modified
# netnum.
# in - inetnum
#
    if ($key eq "in") {
	$j = 0;
	local($onenet) = 0;
	@nets = split(/\s+/, $value);
	if ($#nets == 0) {
	    # check the single network
	    $onenet = 1;
	    $add[0] = $nets[0];
	} elsif ($#nets == 2 && $nets[1] == "-") {
	    $add[0] = $nets[0];
	    $add[1] = $nets[2];
	} elsif ($#nets == 1 ) {
	    $add[0] = $nets[0];
	    $add[1] = $nets[1]; 
	    $mod = 1;
	} else {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - illegal network $value\n";
	}
	foreach $j (0..$#add) {
	    if (!&isnetnum($add[$j])) {	
		return $O_ERROR, 
		"syntax error in \"$ATTL{$key}\" - ".
		    "illegal network \"$add[$j]\"\n";
	    }
	}
	if(!$onenet && (&quad2int($add[1])) < (&quad2int($add[0]))) {
	    return $O_ERROR, 
	    "error in \"$ATTL{$key}\" - range is illegal ".
		"- end of block is too low\n";
	}
	
	if($mod) {
	    $new = $add[0]." - ".$add[1];
	    $object{$key} = $new;
	    return $O_WARNING, "\"$ATTL{$key}\" value ".
		"\"$value\" changed to \"$new\"\n";
	}
	if(!$onenet) {
	    $new = $add[0]." - ".$add[1];
	    if($new ne $value) {
		$object{$key} = $new;
		return $O_WARNING, "\"$ATTL{$key}\" value ".
		    "\"$value\" changed to \"$new\"\n";
	    }
	} 
	return;
    }
#
# la - localas
#
    if ($key eq "la") {
	if(!&isasnum($value)) {
	  return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }  

#
# lo - location
#

    if ($key eq "lo") {
	if ($value !~ /^[a-zA-Z0-9\-\.\ \,\(\)\&\'\"\/]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }

# 
# Added this in for now - maybe removed at a later date.
# This is MERIT/RA special.
# lr - local-route
#
    if ($key eq "lr") {
	local(@list) = split(/\s+/, $value);
	if (!&isasnum($list[0])) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\" - $list[0] is not a valid peer";
	} 
	foreach (1..$#list) {
	    if($list[$_] !~ /^\d+:\d+(\(\d+\))*$/) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\" - $list[$_] is invalid";
	    }
	}
	return;
    }
	
#
# ma - maintainer
#
    if ($key eq "ma") {
	if ($value !~ /^[A-Z0-9\-]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}    
	return;
    }
#
# mb - mnt-by
#
    if ($key eq "mb") {

	if ($value !~ /^[A-Z0-9\-\s+]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}    
	# Check whether all mentioned maintainer values are actually
	# present in the database. This is yucky, but %ExistMaintainer
	# is sneakily built in GetMaintainer to speed things up....
	# As everything, these are only kept per ONE update message (which
	# can of course have multiple objects ....)

	local($status) = 0;
	local($notfound) = "";
	
	foreach (split(/\s+/, $value)) {
            next if
                ($ExistMaintainer{$_} ||
		 ($value eq $object{"mt"}) ||
		 &GetMaintainer($_, $object{"so"}));
            $notfound .= "$_ ";
            $status = 1;
        }
	return $O_ERROR,
	"unknown maintainer(s) \"$notfound\" referenced" if $status;
	return;
    }
#
#  mt - mntner
#
    if ($key eq "mt") {
	if ($value !~ /^[A-Z0-9\-]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}    
	return;
    }
#
# mn - mnt-nfy
#
    if ($key eq "mn") {
	if (!&isemail($value)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" ".
		"- \"$value\" is not in \(RFC822\) format";
	}
	return;
    }
#
# na - netname OR aa - as-name
#
    if ($key eq "na" || $key eq "aa") {
	local($cur) = $value;
	local($changed) = 1 if $object{$key} =~ tr/a-z/A-Z/;
	local($changed) = 1 if $object{$key} =~ tr/\./\-/;
	local($changed) = 1 if $object{$key} =~ tr/\_/\-/;
	if ($object{$key} !~ /^[A-Z0-9][A-Z0-9\-]+$/) {
	    $object{$key} = $cur;
	    return $O_ERROR, "illegal $ATTL{$key} \"$cur\"";
	}
	else {
	    if ($changed) {
		$value = $object{$key};
		return $O_WARNING, "\"$cur\" changed to \"$value\"";
	    }
	}
	return;
    }
#
# Will need to change when the NIC-handle syntax is fixed.
#
# nh - nic-hdl
#
    if ($key eq "nh") {
	local(%preventry) = ();
	local($matches) = 0;
	local($newname) = "";
	local($curname) = "";

	local($uppercased) = 0;
	if ($DOHANDLE) {
	    if ($value =~ /^[Aa][Ss][Ss][Ii][Gg][Nn]\s*(.*)$/) {
		if ($1) {
		    if (!&ishandle($1)) {
			return $O_ERROR, "syntax error in requested nichandle";
		    }
		} 
		return;
	    } 
	}
	if ($object{$key} =~ tr/a-z/A-Z/) {
	    $value = $object{$key};
	    $uppercased = 1;
	}

	if (!&ishandle($value)) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	if ($uppercased) {
	    return $O_WARNING, "\"$ATTL{$key}\" value uppercased";
	}

	local($goandfind) = $value;
	$goandfind =~ tr/A-Z/a-z/;

	($matches, %preventry) =
	    &ExistsObject($goandfind, "pn", $object{"so"} , 1);

	if ($matches) {
	    if (&enukey(*preventry) =~ /\t$goandfind$/) {
		$curname = $preventry{"pn"};
		$curname =~ tr/A-Z/a-z/;
		$curname =~ s/\s+/ /g;
		$newname = $object{"pn"};
		$newname =~ tr/A-Z/a-z/;
		$newname =~ s/\s+/ /g;
		if ($curname ne $newname) {
		    return $O_ERROR, 
		    "nic-hdl \"$value\" already assigned to $preventry{\"pn\"}";
		}
	    }
	}
	return;
    }
#
# ni - nsf-in
#
    if ($key eq "ni") {
	if ($value !~ /^[(\d=\d+)\s*]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# no - nsf-out
#
    if ($key eq "no") {
	if ($value !~ /^[(\d=\d+)\s*]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# ns - nserver
# 
    if ($key eq "ns") {
	@list = ();
	@list = split(/\s+/,$value);
	$j = 0;
	foreach $j (0..$#list) {
	    if (!&isdomname($list[$j])) {
		if ($list[$j] !~ 
		    /^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*$/) {
		    return $O_ERROR, 
		    "illegal nameserver in \"$ATTL{$key}\" ".
			"component \"$list[$j]\"";
		}
	    }
	}
	return;
    }
#
# ny - notify
#
    if ($key eq "ny") {
	if (!&isemail($value)) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - ".
		"\"$value\" is not in \(RFC822\) format";
	}
	return;
    }
#
# op - op-phone
# of - op-fa
# ph - phone
# fx - fax-no
#

    if (($key eq "op") || ($key eq "of") || ($key eq "ph") || ($key eq "fx")) {
	if (!(&isphone($value))) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# om - op-mail
#


    if ($key eq "om") {
	if (!(&isemail($value))) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# or - origin
#
    if ($key eq "or") {
	$value =~ tr/a-z/A-Z/;
	if (!&isasnum($value)) {
	    return $O_ERROR,
	    "syntax error in \"$ATTL{$key}\" - $value is not a valid AS";
	}
	if (($object{$key} =~ tr/a-z/A-Z/)) {
	    return $O_WARNING, "\"$ATTL{$key}\" value uppercased";
	}	
	return;
    }
#
# pe - peer
#
    if ($key eq "pe") {
	local(@peer) = split(/\s+/, $value);
	if($value =~ /localas/) {
	    if($peer[3] ne "localas" || !&isasnum($peer[4])) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\"".
		    " - localas error for $value";
	    }
	}
	elsif (!&isipaddr($peer[0]) || 
	       !&isasnum($peer[1]) ||
	       !&ispeerkeyword($peer[2]) ) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\" - $value";
	}
	return;
    }
#
# pn - person
#
    if ($key eq "pn") {
	local(@names) = split(/\s+/, $value);
	if ($#names == 0) {
	    return $O_ERROR, 
	    "syntax error in \"$ATTL{$key}\" - ".
		"must contain at least two components";
	}
	foreach $j (0..$#names) {
	    if (!&isname($j)) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	    }
	}
	return;
    }
#
# rl - routpr-l
#
    if ($key eq "rl") {
	if ($value !~ /^[A-Z0-9\-\ ]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# rm - remarks
#
    if ($key eq "rm") {
	if ($value !~ /^.*$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
#  rp - rout-pr
#
    if ($key eq "rp") {
	if ($value !~ /^[A-Z0-9\-]+$/) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# rt - route
#
# A little messy - all the work is done in net2net.pl
#
    if ($key eq "rt") {
        local($i) = 0;
	local($NETMASK) = "[nN]*[eE]*[tT]*[mM][aA][sS][kK]";
	local($HEX) = "[0-9a-fA-F]";
	local($HEXMASK) = "0x$HEX$HEX$HEX$HEX$HEX$HEX$HEX$HEX";
	local($IPADDR) = "\\d+\\.\\d+\\.\\d+\\.\\d+";

	if ($value =~ /^$IPADDR$/) {
	    local($stat, $msg, @str) = &clasfn_to_netpre($value);
	    if($stat == $NOK) {
		return $O_ERROR, "$msg\n";
	    } else {
		$object{$key} = $str[0];
		return $O_WARNING, "$ATTL{$key} re-written to $str[0] from $value\n";
	    }
	} elsif ($value =~ /^$IPADDR\s+\-\s+$IPADDR$/) {
	    local($stat, $msg, @str) = &clasfr_to_netpre($value);
	    if($stat == $NOK) {
		return $O_ERROR, "$msg\n";
	    } else {
		if($#str >= 1) {
		    $msg = "$value is not CIDR aligned\n".
			"resubmit the following seperate objects\n";
		    foreach $i (0..$#str) {
			$msg .= "$str[$i]\n";
		    }
		    return $O_ERROR, "$msg\n";
		} else {
		    $object{$key} = $str[0];
		    return $O_WARNING, "$ATTL{$key} re-written to $str[0] from $value\n";
		}
	    }
	} elsif ($value =~ /^$IPADDR\s+($NETMASK)*\s*$IPADDR$/ 
		 || $value =~/^$IPADDR\s+($NETMASK)*\s*$HEXMASK$/) {
	    local($stat, $msg, @str) = &netmask_to_netpre($value);
	    if($stat == $NOK) {
		return $O_ERROR, "$msg\n";
	    } else {
		$object{$key} = $str[0];
		return $O_WARNING,
		"$ATTL{$key} \"$value\" re-written to $str[0] from $value\n";
	    }
	} else {
	    local($stat, $msg, @str) = &netpre_verify($value);
	    if($stat == $NOK) {
		return $O_ERROR, "$msg\n";
	    }
	}
	return;
    }
#
# Need to really check against the DNS eventually
#
# rz - rev-srv
#
    if ($key eq "rz") {
	@list = ();
	@list = split(/\s+/,$value);
	$j = 0;
	foreach $j (0..$#list) {
	    if (!&isdomname($list[$j])) {
		return $O_ERROR, "illegal nameserver in $value";
	    }
	}
	return;
    }
#
# sd - sub-dom
#
    if ($key eq "sd") {
	foreach (split(/\s+/, $value)) {
	    if (!&issubdomname($_)) {
		return $O_ERROR, "syntax error in \"$ATTL{$key}\": $_";
	    }
	}
	return;
    }
#
# so - source
#
    if ($key eq "so") {
	if (!$DBFILE{$value}) {
	    return $O_ERROR, "unknown source \"$value\"";
	}
	if (!$CANUPD{$value}) {
	    return $O_ERROR, "cannot update entry with source \"$value\"";
	}
	return;
    }

#
# st - status
#
    if ($key eq "st") {
	if ($value =~ /^d$|^[Dd][Ee][Ll][Ee][Gg][Aa][Tt][Ee][Dd]$/) {
	    $object{$key} = "D";
	    return $O_WARNING, "\"$ATTL{$key}\" changed to \"D\"";
	}
	if ($value =~ /^r$|^[Rr][Ee][Ss][Ee][Rr][Vv][Ee][Dd]$/) {
            $object{$key} = "R";
            return $O_WARNING, "\"$ATTL{$key}\" changed to \"R\"";
        }
	if ($value =~ /^[Aa]$|^[Aa][Ss][Ss][Ii][Gg][Nn][Ee][Dd]$/) {
	    $object{$key} = "";
	    return $O_WARNING, "\"$ATTL{$key}\" has default value; removed";
	}
	if ($value =~ /^D$|^R$/) {
	    return 1;
	}
	return $O_WARNING, "\"$ATTL{$key}\" has an illegal value";
    }

#
# tc - tech-c
#
    if ($key eq "tc") {
	if (!&isname($value)) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }
#
# tr - as-transit
#
    if ($key eq "tr") {
	return;
    }
#
# wd - withdrawn
#
    if ($key eq "wd") {
	if($value !~ /^(\d\d)(\d\d)(\d\d)$/) {
	    return $O_ERROR, 
	    "date part of \"$ATTL{$key}\" not in YYMMDD format";
	}
	# 1988 is the start of the world. This is where we test for proper
	# date values of YYMMDD

	if (($1 < 88) || ($2 > 12) || ($3 > 31)) {
	    return $O_ERROR, 
	    "date part of \"$ATTL{$key}\" is not a valid YYMMDD value";
	}
	local($s, $m, $h, $md, $mo, $y) = localtime(time);
	$mo += 1;
	$md = "0".$md unless $md > 9;
	$mo = "0".$mo unless $mo > 9;
	$y = "0".$y unless $y > 9;
	local($curdate) = "$y$mo$md";

	if ($value gt $curdate) {
	    return $O_ERORR, 
	    "date in \"$ATTL{$key}\" ($date) is in the future";
	}
	return;
    }
#
# zc - zone-c
#
    if ($key eq "zc") {
	if (!&isname($value)) {
	    return $O_ERROR, "syntax error in \"$ATTL{$key}\"";
	}
	return;
    }

#
# These are not checked and not used, just in here for clarity
#
#
# ue - *ERROR*
#   
    if ($key eq "ue") {
	return;
    }
#
# uf - u-from (NOT USED)
#    
    if ($key eq "uf") {
	return;
    }
#
# ui - msg-id (NOT USED)
#
    
    if ($key eq "ui") {
	return;
    }
#
# uw - WARNING
#
    if ($key eq "uw") {
	return;
    }
}
1;
