<?php
# bookmarker RCS version info:
# $Id: class.Validator.inc,v 1.7 2000/04/10 14:49:56 prenagha Exp $
/*
	Validator 1.2  1999/03/05 CDI

	A class for validating common data from forms
	Copyright (c) 1999 CDI, cdi@thewebmasters.net All Rights Reserved

*/

class Validator
{
	var $ERROR	=	"";
	var $CLEAR	=	false;

	function Validator ()
	{
		return;
	}

	function clear_error ()
	{
		$this->ERROR = "";
	}

//	************************************************************
//	Checks a string for whitespace. True or false

	function has_space ($text)
	{
		if( ereg("[ 	]",$text) )
		{
			return true;
		}

		return false;		
	}

//	************************************************************

	function chconvert ($fragment)
	{
		if      ($fragment == 7) { $result = "rwx"; }
		elseif  ($fragment == 6) { $result = "rw-"; }
		elseif  ($fragment == 5) { $result = "r-x"; }
		elseif  ($fragment == 4) { $result = "r--"; }
		elseif  ($fragment == 3) { $result = "-wx"; }
		elseif  ($fragment == 2) { $result = "-w-"; }
		elseif  ($fragment == 1) { $result = "--x"; }
		elseif  ($fragment == 0) { $result = "---"; }

		else	{ $result = "unk"; }

		return($result);

    }


//	************************************************************

	function get_perms ($fileName )
	{
		if($this->CLEAR) { $this->clear_error(); }

		$atrib = array();

		$perms = fileperms($fileName);

		if(!$perms)
		{
			$this->ERROR = "get_perms: Unable to obtain file perms on [$fileName]";
			return false;
		}

		$octal = sprintf("%lo", ($perms & 07777) );

		$one = substr($octal,0,1);
		$two = substr($octal,1,1);
		$three = substr($octal,2,1);

		$user  = $this->chconvert($one);
		$group = $this->chconvert($two);
		$other = $this->chconvert($three);

		if(is_dir($fileName))
		{
			$user = "d$user";
		}

		$atrib = array(
						"octal"	=>	$octal,
						"user"	=>	$user,
						"group"	=>	$group,
						"other"	=>	$other
					);

		return $atrib;
	}

//	************************************************************

	function is_sane ($filename)
	{
		if($this->CLEAR) { $this->clear_error(); }

		if (!file_exists($filename))
		{
			$this->ERROR = "File does not exist";
			return false;
		}
		if (!is_readable($filename))
		{
			$this->ERROR = "File is not readable";
			return false;
		}
		if(!is_writeable($filename))
		{
			$this->ERROR = "File is not writeable";
			return false;
		}
		if(is_dir($filename))
		{
			$this->ERROR = "File is a directory";
			return false;
		}
		if(is_link($filename))
		{
			$this->ERROR = "File is a symlink";
			return false;
		}

		return true;
	}

//	************************************************************
//	Strips whitespace (tab or space) from a string

	function strip_space ($text)
	{
		$Return = ereg_replace("([ 	]+)","",$text);
		return ($Return);
	}

//	************************************************************
//	Returns true if string contains only numbers

	function is_allnumbers ($text)
	{
		if( (gettype($text)) == "integer")	{ return true; }

		$Bad = $this->strip_numbers($text);

		if(empty($Bad))
		{
			return true;
		}
		return false;
	}

//	************************************************************
//	Strip numbers from a string

	function strip_numbers ($text)
	{
		$Stripped = eregi_replace("([0-9]+)","",$text);
		return ($Stripped);
	}

//	************************************************************
//	Returns true if string contains only letters

	function is_allletters ($text)
	{
		$Bad = $this->strip_letters($text);
		if(empty($Bad))
		{
			return true;
		}

		return false;
	}


//	************************************************************
//	Strips letters from a string

	function strip_letters ($text)
	{
		$Stripped = eregi_replace("([A-Z]+)","",$text);
		return $Stripped;
	}

//	************************************************************
//	Checks for HTML entities in submitted text.
//	If found returns true, otherwise false. HTML specials are:
//
//		"	=>	&quot;
//		<	=>	&lt;
//		>	=>	&gt;
//		&	=>	&amp;
//
//	The presence of ",<,>,&  will force this method to return true.
//
	function has_html ($text = "")
	{
		if(empty($text))
		{
			return false;
		}
		$New = htmlspecialchars($text);
		if($New == $text)
		{
			return false;
		}
		return true;
	}

//	************************************************************
//	strip_html()
//
//	Strips all html entities, attributes, elements and tags from
//	the submitted string data and returns the results.
//
//	Can't use a regex here because there's no way to know
//	how the data is laid out. We have to examine every character
//	that's been submitted. Consequently, this is not a very
//	efficient method. It works, it's very good at removing
//	all html from the data, but don't send gobs of data
//	at it or your program will slow to a crawl.

//	If you're stripping HTML from a file, use PHP's fgetss()
//	and NOT this method, as fgetss() does the same thing
//	about 100x faster.

	function strip_html ($text = "")
	{
		if( (!$text) or (empty($text)) )
		{
			return "";
		}
		$outside = true;
		$rawText = "";
		$length = strlen($text);
		$count = 0;

		for($count=0; $count < $length; $count++)
		{
			$digit = substr($text,$count,1);
			if(!empty($digit))
			{
				if( ($outside) and ($digit != "<") and ($digit != ">") )
				{
					$rawText .= $digit;
				}
				if($digit == "<")
				{
					$outside = false;
				}
				if($digit == ">")
				{
					$outside = true;
				}
			}
		}
		return $rawText;
	}

//	************************************************************
//	Returns true of the submitted text has meta characters in it
//	. \\ + * ? [ ^ ] ( $ )
//
// 
	function has_metas ($text = "")
	{
		if(empty($text))
		{
			return false;
		}

		$New = quotemeta($text);

		if($New == $text)
		{
			return false;
		}

		return true;
	}


//	************************************************************
//	Strips "  . \\ + * ? [ ^ ] ( $ )  " from submitted string
//
//	Metas are a virtual MINE FIELD for regular expressions,
//  see custom_strip() for how they are removed
 
	function strip_metas ($text = "")
	{
		if(empty($text))
		{
			return false;
		}

		$Metas = array( '.','+','*','?','[','^',']','(','$',')' );
		$text = stripslashes($text);
		$New = $this->custom_strip($Metas,$text);
		return $New;
	}

//	************************************************************
//	$Chars must be an array of characters to remove.
//	This method is meta-character safe.

	function custom_strip ($Chars, $text = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if(empty($text))
		{
			return false;
		}

		if( (gettype($Chars)) != "array")
		{
			$this->ERROR = "custom_strip: [$Chars] is not an array";
			return false;
		}

		while ( list ( $key,$val) = each ($Chars) )
		{
			if(!empty($val))
			{
				// str_replace is meta-safe, ereg_replace is not
				$text = str_replace($val,"",$text);
			}
		}

		return $text;
	}

//	************************************************************
//	Array_Echo will walk through an array,
//	continuously printing out key value pairs.
//
//	Multi dimensional arrays are handled recursively.

	function array_echo ($MyArray, $Name = "Array")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if( (gettype($MyArray)) != "array") { return; }

		$count = 0;

		while ( list ($key,$val) = each ($MyArray) )
		{
			if($count == 0)
			{
				echo "\n\n<P><TABLE BORDER=1 CELLPADDING=0 CELLSPACING=0 COLS=8\n";
				echo "><TR><TD VALIGN=TOP COLSPAN=4><B>$Name Contents:</B></TD\n";
				echo "><TD COLSPAN=2><B>KEY</B></TD><TD COLSPAN=2><B>VAL</B></TD></TR\n>";
			}
			if( (gettype($val)) == "array")
			{
				$NewName = "$key [$Name $count]";
				$NewArray = $MyArray[$key];
				echo "</TD></TR></TABLE\n\n>";
				$this->array_echo($NewArray,$NewName);
				echo "\n\n<P><TABLE BORDER=1 CELLPADDING=0 CELLSPACING=0 COLS=8\n";
				echo "><TR><TD VALIGN=TOP COLSPAN=4><B>$Name Continued:</B></TD\n";
				echo "><TD COLSPAN=2><B>KEY</B></TD><TD COLSPAN=2><B>VAL</B></TD></TR\n>";
			}
			else
			{
				echo "<TR>";
				$Col1 = sprintf("[%s][%0d]",$Name,$count);
				$Col2 = $key;
				if(empty($val))	{ $val = '&nbsp;'; }
				$Col3 = $val;
				echo "<TD COLSPAN=4>$Col1</TD>";
				echo "<TD COLSPAN=2>$Col2</TD\n>";
				echo "<TD COLSPAN=2>$Col3</TD></TR\n\n>";
			}
			$count++;
		}
		echo "<TR><TD COLSPAN=8><B>Array [$Name] complete.</B></TD></TR\n>";
		echo "</TD></TR></TABLE\n\n>";
		return;
	}


//	************************************************************
//	Valid email format? true or false
//	This checks the raw address, not RFC 822 addresses.

//	Looks for [something]@[valid hostname with DNS record]

	function is_email ($Address = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if(empty($Address))
		{
			$this->ERROR = "is_email: No email address submitted";
			return false;
		}

		if(!ereg("@",$Address))
		{
			$this->ERROR = "is_email: Invalid, no @ symbol in string";
			return false;
		}
		
		list($User,$Host) = split("@",$Address);

		if ( (empty($User)) or (empty($Address)) )
		{
			$this->ERROR = "is_email: missing data [$User]@[$Host]";
			return false;
		}
		if( ($this->has_space($User)) or ($this->has_space($Host)) )
		{
			$this->ERROR = "is_email: Whitespace in [$User]@[$Host]";
			return false;
		}

		// Can't look for an MX only record as that precludes
		// CNAME only records. Thanks to everyone that slapped
		// me upside the head for this glaring oversite. :)

		if(!$this->is_host($Host,"ANY"))		{ return false; }

		return true;
	}

//	************************************************************
//	Valid URL format? true or false

//	Checks format of a URL - does NOT handle query strings or
//	urlencoded data.

	function is_url ($Url = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if (empty($Url))
		{
			$this->ERROR = "is_url: No URL submitted";
			return false;
		}

		// Wow, the magic of parse_url!

		$UrlElements = parse_url($Url);
		if( (empty($UrlElements)) or (!$UrlElements) )
		{
			$this->ERROR = "is_url: Parse error reading [$Url]";
			return false;
		}

		$scheme		= $UrlElements[scheme];
		$HostName	= $UrlElements[host];

		if(empty($scheme))
		{
			$this->ERROR = "is_url: Missing protocol declaration";
			return false;
		}

		if(empty($HostName))
		{
			$this->ERROR = "is_url: No hostname in URL";
			return false;
		}

		if (!eregi("^(ht|f)tp",$scheme))
		{
			$this->ERROR = "is_url: No http:// or ftp://";
			return false;
		}

## padraic renaghan change for bookmarker ver 1.4 bug 69
## if hostname is an ip address, check the validity of
## the ip address, otherwise check as if host name
## is specified.
    if (ereg("[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+",$HostName)) {
     if(!$this->is_ipaddress($HostName))	{ return false; }
    } else {
		 if(!$this->is_hostname($HostName))	  { return false; }
	  }

		return true;
	}

//	************************************************************
//	URL responds to requests? true or false
//	This will obviously fail if you're not connected to
//	the internet, or if there are connection problems. (firewall etc)

	function url_responds ($Url = "")
	{
		global $php_errormsg;

		if($this->CLEAR) { $this->clear_error(); }

		if(empty($Url))
		{
			$this->ERROR = "url_responds: No URL submitted";
			return false;
		}

		if(!$this->is_url($Url)) { return false; }

		$fd = @fopen($Url,"r");
		if(!$fd)
		{
			$this->ERROR = "url_responds: Failed : $php_errormsg";
			return false;
		}
		else
		{
			@fclose($fd);
			return true;
		}
	}

//	************************************************************
//	Valid phone number? true or false
//	Tries to validate a phone number

//	Strips (,),-,+ from number prior to checking
//	Less than 7 digits = fail
//	More than 13 digits = fail
//	Anything other than numbers after the stripping = fail

	function is_phone ($Phone = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if(empty($Phone))
		{
			$this->ERROR = "is_phone: No Phone number submitted";
			return false;
		}

		$Num = $Phone;
		$Num = $this->strip_space($Num);
		$Num = eregi_replace("(\(|\)|\-|\+)","",$Num);
		if(!$this->is_allnumbers($Num))
		{
			$this->ERROR = "is_phone: bad data in phone number";
			return false;
		}

		if ( (strlen($Num)) < 7)
		{
			$this->ERROR = "is_phone: number is too short [$Num][$Phone]";
			return false;
		}

		// 000 000 000 0000
 		// CC  AC  PRE SUFX = max 13 digits

		if( (strlen($Num)) > 13)
		{
			$this->ERROR = "is_phone: number is too long [$Num][$Phone]";
			return false;
		}

		return true;
	}

//	************************************************************
//	Valid, fully qualified hostname? true or false
//	Checks the -syntax- of the hostname, not it's actual
//	validity as a reachable internet host

	function is_hostname ($hostname = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		$web = false;

		if(empty($hostname))
		{
			$this->ERROR = "is_hostname: No hostname submitted";
			return false;
		}

		// Only a-z, 0-9, and "-" or "." are permitted in a hostname

		// Patch for POSIX regex lib by Sascha Schumann sas@schell.de
		$Bad = eregi_replace("[-A-Z0-9\.]","",$hostname);

		if(!empty($Bad))
		{
			$this->ERROR = "is_hostname: invalid chars [$Bad]";
			return false;
		}

		// See if we're doing www.hostname.tld or hostname.tld
		if(eregi("^www\.",$hostname))
		{
			$web = true;
		}

		// double "." is a not permitted
		if(ereg("\.\.",$hostname))
		{
			$this->ERROR = "is_hostname: Double dot in [$hostname]";
			return false;
		}
		if(ereg("^\.",$hostname))
		{
			$this->ERROR = "is_hostname: leading dot in [$hostname]";
			return false;
		}

		$chunks = explode(".",$hostname);

		if( (gettype($chunks)) != "array")
		{
			$this->ERROR = "is_hostname: Invalid hostname, no dot seperator [$hostname]";
			return false;
		}

		$count = ( (count($chunks)) - 1);

		if($count < 1)
		{
			$this->ERROR = "is_hostname: Invalid hostname [$count] [$hostname]\n";
			return false;
		}

		// Bug that can't be killed without doing an is_host,
		// something.something will return TRUE, even if it's something
		// stupid like NS.SOMETHING (with no tld), because SOMETHING is
		// construed to BE the tld.  The is_bigfour and is_country
		// checks should help eliminate this inconsistancy. To really
		// be sure you've got a valid hostname, do an is_host() on it.

		if( ($web) and ($count < 2) )
		{
			$this->ERROR = "is_hostname: Invalid hostname [$count] [$hostname]\n";
			return false;
		}

		$tld = $chunks[$count];

		if(empty($tld))
		{
			$this->ERROR = "is_hostname: No TLD found in [$hostname]";
			return false;
		}

		if(!$this->is_bigfour($tld))
		{
			if(!$this->is_country($tld))
			{
				$this->ERROR = "is_hostname: Unrecognized TLD [$tld]";
				return false;
			}
		}
		

		return true;
	}

//	************************************************************

	function is_bigfour ($tld)
	{
		if(empty($tld))
		{
			return false;
		}
		if(eregi("^\.",$tld))
		{
			$tld = eregi_replace("^\.","",$tld);
		}
		$BigFour = array (com=>com,edu=>edu,net=>net,org=>org,gov=>gov,mil=>mil,int=>int);
		$tld = strtolower($tld);

		if(isset($BigFour[$tld]))
		{
			return true;
		}

		return false;
	}

//	************************************************************
//	Hostname is a reachable internet host? true or false

	function is_host ($hostname = "", $type = "ANY")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if(empty($hostname))
		{
			$this->ERROR = "is_host: No hostname submitted";	
			return false;
		}

		if(!$this->is_hostname($hostname))	{ return false; }

		if(!checkdnsrr($hostname,$type))
		{
			$this->ERROR = "is_host: no DNS records for [$hostname].";
			return false;
		}

		return true;
	}

//	************************************************************
//	Dotted quad IPAddress within valid range? true or false
//	Checks format, leading zeros, and values > 255
//	Does not check for reserved or unroutable IPs.

	function is_ipaddress ($IP = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if(empty($IP))
		{
			$this->ERROR = "is_ipaddress: No IP address submitted";
			return false;
		}
		//	123456789012345
		//	xxx.xxx.xxx.xxx

		$len = strlen($IP);
		if( $len > 15 )
		{
			$this->ERROR = "is_ipaddress: too long [$IP][$len]";
			return false;
		}

		$Bad = eregi_replace("([0-9\.]+)","",$IP);

		if(!empty($Bad))
		{
			$this->ERROR = "is_ipaddress: Bad data in IP address [$Bad]";
			return false;
		}
		$chunks = explode(".",$IP);
		$count = count($chunks);

		if ($count != 4)
		{
			$this->ERROR = "is_ipaddress: not a dotted quad [$IP]";
			return false;
		}

		while ( list ($key,$val) = each ($chunks) )
		{
			if(ereg("^0",$val))
			{
				$this->ERROR = "is_ipaddress: Invalid IP segment [$val]";
				return false;
			}
			$Num = $val;
			settype($Num,"integer");
			if($Num > 255)
			{
				$this->ERROR = "is_ipaddress: Segment out of range [$Num]";
				return false;
			}

		}

		return true;

	}	// end is_ipaddress

//	************************************************************
//	IP address is valid, and resolves to a hostname? true or false

	function ip_resolves ($IP = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if(empty($IP))
		{
			$this->ERROR = "ip_resolves: No IP address submitted";
			return false;
		}

		if(!$this->is_ipaddress($IP))
		{
			return false;
		}

		$Hostname = gethostbyaddr($IP);

		if($Hostname == $IP)
		{
			$this->ERROR = "ip_resolves: IP does not resolve.";
			return false;
		}

		if($Hostname)
		{
			if(!checkdnsrr($Hostname))
			{
				$this->ERROR = "is_ipaddress: no DNS records for resolved hostname [$Hostname]";
				return false;
			}
			if( (gethostbyname($Hostname)) != $IP )
			{
				$this->ERROR = "is_ipaddress: forward:reverse mismatch, possible forgery";
				//	Non-fatal, but it should be noted.
			}
		}
		else
		{
			$this->ERROR = "ip_resolves: IP address does not resolve";
			return false;
		}

		return true;
	}


//	************************************************************

	function browser_gen ()
	{
		if($this->CLEAR) { $this->clear_error(); }
		$generation = "UNKNOWN";

		$client = getenv("HTTP_USER_AGENT");
		if(empty($client))
		{
			$this->ERROR = "browser_gen: No User Agent for Client";
			return $generation;
		}

		$client = $this->strip_metas($client);

		$agents = array(
			'Anonymizer'		=>	"ANONYMIZER",
			'Ahoy'				=>	"SPIDER",
			'Altavista'			=>	"SPIDER",
			'Anzwers'			=>	"SPIDER",
			'Arachnoidea'		=>	"SPIDER",
			'Arachnophilia'		=>	"SPIDER",
			'ArchitextSpider'	=>	"SPIDER",
			'Backrub'			=>	"SPIDER",
			'CherryPicker'		=>	"SPAMMER",
			'Crescent'			=>	"SPAMMER",
			'Duppies'			=>	"SPIDER",
			'EmailCollector'	=>	"SPAMMER",
			'EmailSiphon'		=>	"SPAMMER",
			'EmailWolf'			=>	"SPAMMER",
			'Extractor'			=>	"SPAMMER",
			'Fido'				=>	"SPIDER",
			'Fish'				=>	"SPIDER",
			'GAIS'				=>	"SPIDER",
			'Googlebot'			=>	"SPIDER",
			'Gulliver'			=>	"SPIDER",
			'HipCrime'			=>	"SPAMMER",
			'Hamahakki'			=>	"SPIDER",
			'ia_archive'		=>	"SPIDER",
			'IBrowse'			=>	"THIRD",
			'Incy'				=>	"SPIDER",
			'InfoSeek'			=>	"SPIDER",
			'KIT-Fireball'		=>	"SPIDER",
			'Konqueror'			=>	"THIRD",
			'libwww'			=>	"SECOND",
			'LocalEyes'			=>	"SECOND",
			'Lycos'				=>	"SPIDER",
			'Lynx'				=>	"SECOND",
			'Microsoft.URL'		=>	"SPAMMER",
			'MOMspider'			=>	"SPIDER",
			'Mozilla/1'			=>	"FIRST",
			'Mozilla/2'			=>	"SECOND",
			'Mozilla/3'			=>	"THIRD",
			'Mozilla/4'			=>	"FOURTH",
			'Mozilla/5'			=>	"FIFTH",
			'Namecrawler'		=>	"SPIDER",
			'NICErsPRO'			=>	"SPAMMER",
			'Scooter'			=>	"SPIDER",
			'sexsearch'			=>	"SPIDER",
			'Sidewinder'		=>	"SPIDER",
			'Slurp'				=>	"SPIDER",
			'SwissSearch'		=>	"SPIDER",
			'Ultraseek'			=>	"SPIDER",
			'WebBandit'			=>	"SPAMMER",
			'WebCrawler'		=>	"SPIDER",
			'WiseWire'			=>	"SPIDER",
			'Mozilla/3.0 (compatible; Opera/3'	=>	"THIRD"
			);

		while ( list ($key,$val) = each ($agents) )
		{
			$key = $this->strip_metas($key);
			if(eregi("^$key",$client))
			{
				unset($agents);
				return $val;
			}
		}

		unset($agents);
		return $generation;
	}

//	************************************************************
//	United States valid state code? true or false

	function is_state ($State = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if(empty($State))
		{
			$this->ERROR = "is_state: No state submitted";
			return false;
		}
		if( (strlen($State)) != 2)
		{
			$this->ERROR = "is_state: Too many digits in state code";
			return false;
		}

		$State = strtoupper($State);

		// 50 states, Washington DC, Puerto Rico and the US Virgin Islands

		$SCodes = array (

				"AK"	=>	1,
				"AL"	=>	1,
				"AR"	=>	1,
				"AZ"	=>	1,
				"CA"	=>	1,
				"CO"	=>	1,
				"CT"	=>	1,
				"DC"	=>	1,
				"DE"	=>	1,
				"FL"	=>	1,
				"GA"	=>	1,
				"HI"	=>	1,
				"IA"	=>	1,
				"ID"	=>	1,
				"IL"	=>	1,
				"IN"	=>	1,
				"KS"	=>	1,
				"KY"	=>	1,
				"LA"	=>	1,
				"MA"	=>	1,
				"MD"	=>	1,
				"ME"	=>	1,
				"MI"	=>	1,
				"MN"	=>	1,
				"MO"	=>	1,
				"MS"	=>	1,
				"MT"	=>	1,
				"NC"	=>	1,
				"ND"	=>	1,
				"NE"	=>	1,
				"NH"	=>	1,
				"NJ"	=>	1,
				"NM"	=>	1,
				"NV"	=>	1,
				"NY"	=>	1,
				"OH"	=>	1,
				"OK"	=>	1,
				"OR"	=>	1,
				"PA"	=>	1,
				"PR"	=>	1,
				"RI"	=>	1,
				"SC"	=>	1,
				"SD"	=>	1,
				"TN"	=>	1,
				"TX"	=>	1,
				"UT"	=>	1,
				"VA"	=>	1,
				"VI"	=>	1,
				"VT"	=>	1,
				"WA"	=>	1,
				"WI"	=>	1,
				"WV"	=>	1,
				"WY"	=>	1
			);

		if(!isset($SCodes[$State]))
		{
			$this->ERROR = "is_state: Unrecognized state code [$State]";
			return false;
		}

		// Lets not have this big monster camping in memory eh?
		unset($SCodes);

		return true;
	}

//	************************************************************
//	Valid postal zip code? true or false

	function is_zip ($zipcode = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		if(empty($zipcode))
		{
			$this->ERROR = "is_zip: No zipcode submitted";
			return false;
		}

		$Bad = eregi_replace("([-0-9]+)","",$zipcode);

		if(!empty($Bad))
		{
			$this->ERROR = "is_zip: Bad data in zipcode [$Bad]";
			return false;
		}
		$Num = eregi_replace("\-","",$zipcode);
		$len = strlen($Num);
		if ( ($len > 10) or ($len < 5) )
		{
			$this->ERROR = "is_zipcode: Invalid length [$len] for zipcode";
			return false;
		}

		return true;

	}

//	************************************************************
//	Valid postal country code?
//	Returns the name of the country, or null on failure
//	Current array recognizes ~232 country codes. 

//	I don't know if all of these are 100% accurate.
//	You don't wanna know how difficult it was just getting
//	this listing in here. :)

	function is_country ($countrycode = "")
	{
		if($this->CLEAR) { $this->clear_error(); }

		$Return = "";

		if(empty($countrycode))
		{
			$this->ERROR = "is_country: No country code submitted";
			return $Return;
		}

		$countrycode = strtolower($countrycode);

		if( (strlen($countrycode)) != 2 )
		{
			$this->ERROR = "is_country: 2 digit codes only [$countrycode]";
			return $Return;
		}

		//	Now for a really big array

		//	Dominican Republic, cc = "do" because it's a reserved
		//	word in PHP. That parse error took 10 minutes of
		//	head-scratching to figure out :)

		//	A (roughly) 3.1 Kbyte array

		$CCodes =	array (

			"do"	=>	"Dominican Republic",
				ad	=>	"Andorra",
				ae	=>	"United Arab Emirates",
				af	=>	"Afghanistan",
				ag	=>	"Antigua and Barbuda",
				ai	=>	"Anguilla",
				al	=>	"Albania",
				am	=>	"Armenia",
				an	=>	"Netherlands Antilles",
				ao	=>	"Angola",
				aq	=>	"Antarctica",
				ar	=>	"Argentina",
				"as"	=>	"American Samoa",
				at	=>	"Austria",
				au	=>	"Australia",
				aw	=>	"Aruba",
				az	=>	"Azerbaijan",
				ba	=>	"Bosnia Hercegovina",
				bb	=>	"Barbados",
				bd	=>	"Bangladesh",
				be	=>	"Belgium",
				bf	=>	"Burkina Faso",
				bg	=>	"Bulgaria",
				bh	=>	"Bahrain",
				bi	=>	"Burundi",
				bj	=>	"Benin",
				bm	=>	"Bermuda",
				bn	=>	"Brunei Darussalam",
				bo	=>	"Bolivia",
				br	=>	"Brazil",
				bs	=>	"Bahamas",
				bt	=>	"Bhutan",
				bv	=>	"Bouvet Island",
				bw	=>	"Botswana",
				by	=>	"Belarus (Byelorussia)",
				bz	=>	"Belize",
				ca	=>	"Canada",
				cc	=>	"Cocos Islands",
				cd	=>	'Congo, The Democratic Republic of the',
				cf	=>	"Central African Republic",
				cg	=>	"Congo",
				ch	=>	"Switzerland",
				ci	=>	"Ivory Coast",
				ck	=>	"Cook Islands",
				cl	=>	"Chile",
				cm	=>	"Cameroon",
				cn	=>	"China",
				co	=>	"Colombia",
				cr	=>	"Costa Rica",
				cs	=>	"Czechoslovakia",
				cu	=>	"Cuba",
				cv	=>	"Cape Verde",
				cx	=>	"Christmas Island",
				cy	=>	"Cyprus",
				cz	=>	'Czech Republic',
				de	=>	"Germany",
				dj	=>	"Djibouti",
				dk	=>	'Denmark',
				dm	=>	"Dominica",
				dz	=>	"Algeria",
				ec	=>	"Ecuador",
				ee	=>	"Estonia",
				eg	=>	"Egypt",
				eh	=>	"Western Sahara",
				er	=>	'Eritrea',
				es	=>	"Spain",
				et	=>	"Ethiopia",
				fi	=>	"Finland",
				fj	=>	"Fiji",
				fk	=>	"Falkland Islands",
				fm	=>	"Micronesia",
				fo	=>	"Faroe Islands",
				fr	=>	"France",
				fx	=>	'France, Metropolitan FX',
				ga	=>	"Gabon",
				gb	=>	'United Kingdom (Great Britain)',
				gd	=>	"Grenada",
				ge	=>	"Georgia",
				gf	=>	"French Guiana",
				gh	=>	"Ghana",
				gi	=>	"Gibraltar",
				gl	=>	"Greenland",
				gm	=>	"Gambia",
				gn	=>	"Guinea",
				gp	=>	"Guadeloupe",
				gq	=>	"Equatorial Guinea",
				gr	=>	"Greece",
				gs	=>	'South Georgia and the South Sandwich Islands',
				gt	=>	"Guatemala",
				gu	=>	"Guam",
				gw	=>	"Guinea-bissau",
				gy	=>	"Guyana",
				hk	=>	"Hong Kong",
				hm	=>	"Heard and McDonald Islands",
				hn	=>	"Honduras",
				hr	=>	"Croatia",
				ht	=>	"Haiti",
				hu	=>	"Hungary",
				id	=>	"Indonesia",
				ie	=>	"Ireland",
				il	=>	"Israel",
				in	=>	"India",
				io	=>	"British Indian Ocean Territory",
				iq	=>	"Iraq",
				ir	=>	"Iran",
				is	=>	"Iceland",
				it	=>	"Italy",
				jm	=>	"Jamaica",
				jo	=>	"Jordan",
				jp	=>	"Japan",
				ke	=>	"Kenya",
				kg	=>	"Kyrgyzstan",
				kh	=>	"Cambodia",
				ki	=>	"Kiribati",
				km	=>	"Comoros",
				kn	=>	"Saint Kitts and Nevis",
				kp	=>	"North Korea",
				kr	=>	"South Korea",
				kw	=>	"Kuwait",
				ky	=>	"Cayman Islands",
				kz	=>	"Kazakhstan",
				la	=>	"Laos",
				lb	=>	"Lebanon",
				lc	=>	"Saint Lucia",
				li	=>	"Lichtenstein",
				lk	=>	"Sri Lanka",
				lr	=>	"Liberia",
				ls	=>	"Lesotho",
				lt	=>	"Lithuania",
				lu	=>	"Luxembourg",
				lv	=>	"Latvia",
				ly	=>	"Libya",
				ma	=>	"Morocco",
				mc	=>	"Monaco",
				md	=>	"Moldova Republic",
				mg	=>	"Madagascar",
				mh	=>	"Marshall Islands",
				mk	=>	'Macedonia, The Former Yugoslav Republic of',
				ml	=>	"Mali",
				mm	=>	"Myanmar",
				mn	=>	"Mongolia",
				mo	=>	"Macau",
				mp	=>	"Northern Mariana Islands",
				mq	=>	"Martinique",
				mr	=>	"Mauritania",
				ms	=>	"Montserrat",
				mt	=>	"Malta",
				mu	=>	"Mauritius",
				mv	=>	"Maldives",
				mw	=>	"Malawi",
				mx	=>	"Mexico",
				my	=>	"Malaysia",
				mz	=>	"Mozambique",
				na	=>	"Namibia",
				nc	=>	"New Caledonia",
				ne	=>	"Niger",
				nf	=>	"Norfolk Island",
				ng	=>	"Nigeria",
				ni	=>	"Nicaragua",
				nl	=>	"Netherlands",
				no	=>	"Norway",
				np	=>	"Nepal",
				nr	=>	"Nauru",
				nt	=>	"Neutral Zone",
				nu	=>	"Niue",
				nz	=>	"New Zealand",
				om	=>	"Oman",
				pa	=>	"Panama",
				pe	=>	"Peru",
				pf	=>	"French Polynesia",
				pg	=>	"Papua New Guinea",
				ph	=>	"Philippines",
				pk	=>	"Pakistan",
				pl	=>	"Poland",
				pm	=>	"St. Pierre and Miquelon",
				pn	=>	"Pitcairn",
				pr	=>	"Puerto Rico",
				pt	=>	"Portugal",
				pw	=>	"Palau",
				py	=>	"Paraguay",
				qa	=>	'Qatar',
				re	=>	"Reunion",
				ro	=>	"Romania",
				ru	=>	"Russia",
				rw	=>	"Rwanda",
				sa	=>	"Saudi Arabia",
				sb	=>	"Solomon Islands",
				sc	=>	"Seychelles",
				sd	=>	"Sudan",
				se	=>	"Sweden",
				sg	=>	"Singapore",
				sh	=>	"St. Helena",
				si	=>	"Slovenia",
				sj	=>	"Svalbard and Jan Mayen Islands",
				sk	=>	'Slovakia (Slovak Republic)',
				sl	=>	"Sierra Leone",
				sm	=>	"San Marino",
				sn	=>	"Senegal",
				so	=>	"Somalia",
				sr	=>	"Suriname",
				st	=>	"Sao Tome and Principe",
				sv	=>	"El Salvador",
				sy	=>	"Syria",
				sz	=>	"Swaziland",
				tc	=>	"Turks and Caicos Islands",
				td	=>	"Chad",
				tf	=>	"French Southern Territories",
				tg	=>	"Togo",
				th	=>	"Thailand",
				tj	=>	"Tajikistan",
				tk	=>	"Tokelau",
				tm	=>	"Turkmenistan",
				tn	=>	"Tunisia",
				to	=>	"Tonga",
				tp	=>	"East Timor",
				tr	=>	"Turkey",
				tt	=>	"Trinidad, Tobago",
				tv	=>	"Tuvalu",
				tw	=>	"Taiwan",
				tz	=>	"Tanzania",
				ua	=>	"Ukraine",
				ug	=>	"Uganda",
				uk	=>	"United Kingdom",
				um	=>	"United States Minor Islands",
				us	=>	"United States of America",
				uy	=>	"Uruguay",
				uz	=>	"Uzbekistan",
				va	=>	"Vatican City",
				vc	=>	"Saint Vincent, Grenadines",
				ve	=>	"Venezuela",
				vg	=>	"Virgin Islands (British)",
				vi	=>	"Virgin Islands (USA)",
				vn	=>	"Viet Nam",
				vu	=>	"Vanuatu",
				wf	=>	'Wallis and Futuna Islands',
				ws	=>	"Samoa",
				ye	=>	'Yemen',
				yt	=>	'Mayotte',
				yu	=>	"Yugoslavia",
				za	=>	"South Africa",
				zm	=>	"Zambia",
				zr	=>	"Zaire",
				zw	=>	"Zimbabwe"
		);


		if(isset($CCodes[$countrycode]))
		{
			$Return = $CCodes[$countrycode];
		}
		else
		{
			$this->ERROR = "is_country: Unrecognized country code [$countrycode]";
			$Return = "";
		}

		// make sure this monster is removed from memory

		unset($CCodes);

		return ($Return);

	}	// end is_country

}	// End class

?>