PHPBuilder - Distribution License Class



RSS Twitter
Snippets Other

Distribution License Class

by: Oliver Lillie
|
May 20, 2005

Version: 0.4

Type: Class

Category: Other

License: GNU General Public License

Description: This class can used to generate keys to control the distribution PHP applications that are enable according to a license. It generates license strings that can bind PHP applications to specific servers or can only be executed during limited time period or to restrictions placed on a home server. The binding to server process attempts to use the server network card MAC address. This feature was tested on servers are Mac OS X (Darwin), Linux, Windows XP, although it may also work for FreeBSD, NetBSD, Solaris. The class also attempts to use information from $_SERVER variable to encode that server name, server IP, server user path and server url to the application folder. This server binding can be disabled if required, and it also possible to block the key being generated for the localhost address (127.0.0.1) The time limiting of the license uses a start period (and a given start offset to allow for time discrepancies) and an expiry date if required. If required when validating a key it is possible to dial home to check the license key on your own server. (see demo2.license.php file) The PHP_OS and PHP_VERSION of the php that the key was generated for is also encrypted into the key. It is also possible to encrypt additional information into the license key. This class is still in development however it is stable.



<?php

	/**
	* Project:		Distrubution License Class
	* File:			class.license.php
	*
	* Copyright (C) 2005 Oliver Lillie
	* 
	* This program is free software; you can redistribute it and/or modify it 
	* under the terms of the GNU General Public License as published by  the Free 
	* Software Foundation; either version 2 of the License, or (at your option) 
	* any later version.
	*
	* This program is distributed in the hope that it will be useful, but 
	* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
	* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 
	* for more details.
	*
	* You should have received a copy of the GNU General Public License along 
	* with this program; if not, write to the Free Software Foundation, Inc., 
	* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA	
	*
	* @link http://www.buggedcom.co.uk/
	* @link http://www.phpclasses.org/browse/package/2298.html
	* @author Oliver Lillie, buggedcom <publicmail at buggedcom dot co dot uk>
	* @version 0.2
	* @history---------------------------------------------
	* v0.4 : 20.05.2005 : dial home methods added
	* v0.3 : 08.05.2005 : mcrypt replaced the encryption method
	* v0.2 : 07.05.2005 : get_mac_address added
	* v0.1 : 20.04.2005 : Created
	*/
	
	/**
	* Methods:
	* 	-- Generating key on server when self installing your app
	*     	setServerVars($array) : sets the server info vars
	*     	generate($start='NOW', $expire_in=31449600, $other_array=array()) : generates a key
	*     	validate($dat_path) : validates the key
	*
	* 	-- Generating Request key on server when user installs app and sends to your server for validation and then
	*	-- your server generates the actuall key to be used.
	*     	setServerVars($array) : sets the server info vars
	*     	generateRequest($license_start='NOW', $license_span=1209600, $other_array=array()) : generates a request key
	*		getRequestData($request_str) : gets the data from the string without generating a key.
	*     	validateRequest($request_str, $other_array=array()) : validates the request key and returns the license key as well as the license request key array
	*
	* Example:
	* 	-- see supplied license.demo.php file
	**/
	
	class distributionLicense {
	
		/**
		* hash key 1 used to encrypt the generate key data.
		* hash key 2 used to encrypt the request data
		* hash key 3 used to encrypt the dial home data
		* NOTE1 : there are three different hash keys for the three different operations
		* NOTE2 : Although mycrpt is in use and typically demands smaller keys than
		* custom functions they should be kept long for none mcrypt encryption
		*
		* @var string
		* @var string
		* @var string
		*/
		var $_HASH_KEY1 	= 'YmUzYWM2sNGU24NbA363zA7IDSDFGDFGB5aVi35BDFGQ3YNO36ycDFGAATq4sYmSFVDFGDFGps7XDYEzGDDw96OnMW3kjCFJ7M+UV2kHe1WTTEcM09UMHHT';
		var $_HASH_KEY2 	= '80dSbqylf4Cu5e5OYdAoAVkzpRDWAt7J1Vp27sYDU52ZBJprdRL1KE0il8KQXuKCK3sdA51P9w8U60wohX2gdmBu7uVhjxbS8g4y874Ht8L12W54Q6T4R4a';
		var $_HASH_KEY3 	= 'ant9pbc3OK28Li36Mi4d3fsWJ4tQSN4a9Z2qa8W66qR7ctFbljsOc9J4wa2Bh6j8KB3vbEXB18i6gfbE0yHS0ZXQCceIlG7jwzDmN7YT06mVwcM9z0vy62T';
		
		/**
		* You may not want to use mcrypt even if your system has it installed
		* make this false to use a regular encryption method
		*
		* @var boolean
		*/
		var $_USE_MCRYPT	= true;

		/**
		* The algorythm to be used by mcrypt
		*
		* @var string
		*/
		var $_ALGORITHM		= 'blowfish';

		/**
		* use server binding vars inited.
		*/
		var $_USE_SERVER;
		var $_SERV;
		var $_MAC;
		var $_ALLOW_LOCAL;
		var $_SERVER_INFO = array();
		
		/**
		* this is the number of required server stats for the key generation to be successfull
		* if the server can't produce this number of details then the key fails to be generated
		* you can set it to however many you wish, the max is 5
		*
		* @var number
		*/
		var $_REQUIRED_URIS	= 2;

		/**
		* use time binding vars inited.
		*/
		var $_USE_TIME;
		
		/**
		* time checking start period difference allowance ie if the user has slightly different time 
		* setting on their server make an allowance for the diff period. carefull to not make it too 
		* much otherwise they could just reset their server to a time period before the license expires.
		*
		* @var number (seconds)
		*/
		var $_START_DIF		= 129600;  
		
		/**
		* use time binding vars for the request key validity.
		*/
		var $_USE_TIME_REQUEST = false;
		
		/**
		* id 1 used to validate license keys
		* id 2 used to validate license key requests
		* id 2 used to validate dial home data
		*
		* @var string
		* @var string
		* @var string
		*/
		# id to check for to validate source
		var $_ID1			= 'nSpkAHRiFfM2hE588eB';
		var $_ID2			= 'NWCy0s0JpGubCVKlkkK';
		var $_ID3			= 'G95ZP2uS782cFey9x5A';

		/**
		* begining and end strings
		*
		* @var strings
		*/
		var $_BEGIN1 		= 'BEGIN LICENSE KEY';
		var $_END1			= 'END LICENSE KEY';

		/**
		* begining and end request strings
		*
		* @var strings
		*/
		var $_BEGIN2 		= 'BEGIN LICENSE REQUEST KEY';
		var $_END2			= 'END LICENSE REQUEST KEY';

		/**
		* wrap key settings
		*
		* @var number
		* @var string
		*/
		var $_WRAPTO		= 80;
		var $_LINEBREAK		= "\r\n";
		var $_PAD			= "-";
		
		/**
		* dial home return query deliminators
		*
		* @var string
		* @var string
		*/
		var $_BEGIN3	 	= '_DATA{';
		var $_END3	 		= '}DATA_';
			
		/**
		* init the key data array.
		*
		* @var array
		*/
		var $_DATA			= array();

		/**
		* init the database value.
		*/
		var $_DB;

		/**
		* the database table prefix.
		*
		* @var array
		*/
		var $_DB_PREFIX		= '';

		/**
		* Constructor
		*
		* @access public 
		* @param $use_mcrypt boolean Determines if mcrypt encryption is used or not (defaults to true, 
		*					 however if mcrypt is not available, it is set to false) 
		* @param $use_time boolean Sets if time binding should be used in the key (defaults to true) 
		* @param $use_server boolean Sets if server binding should be used in the key (defaults to true) 
		* @param $allow_local boolean Sets if server binding is in use then localhost servers are valid (defaults to false) 
		**/
		function distributionLicense($use_mcrypt=true, $use_time=true, $use_server=true, $allow_local=false)
		{
			$this->_USE_MCRYPT			= ($use_mcrypt && function_exists('mcrypt_generic'));
			$this->_USE_TIME			= $use_time;
			$this->_ALLOW_LOCAL			= $allow_local;
			if($use_server)
			{
				$this->_USE_SERVER		= true;
				$this->_SERVER_INFO		= $this->getServerInfo();
				$this->_MAC				= $this->get_mac_address();
			}
		}
		
		/**
		* pad
		*
		* pad out the begin and end seperators
		*
		* @access private 
		* @param $str string The string to be padded
		* @param $pad string The string used to pad either side of the begin and end string (defaults to -) 
  		* @return string Returns the padded string
		**/
		function pad($str)
		{
			$str_len 	= strlen($str);
			$spaces 	= ($this->_WRAPTO-$str_len)/2;
			$str1 = '';
			for($i=0; $i<$spaces; $i++)
			{
				$str1 = $str1.$this->_PAD;
			}
			if($spaces/2 != round($spaces/2))
			{
				$str = substr($str1, 0, strlen($str1)-1).$str;
			}
			else
			{
				$str = $str1.$str;
			}
			$str = $str.$str1;
			return $str;
		}
		
		/**
		* getKey
		*
		* gets the hash key for the current encryption
		*
		* @access private 
		* @param $key_type string The license key type being produced
  		* @return string Returns the hash key
		**/
		function getKey($key_type)
		{
			switch($key_type)
			{
				case 'KEY' :
					return $this->_HASH_KEY1;
				case 'REQUESTKEY' :
					return $this->_HASH_KEY2;
				case 'HOMEKEY' :
					return $this->_HASH_KEY3;
				default :
			}
		}

		/**
		* getBegin
		*
		* gets the begining license key seperator text
		*
		* @access private 
		* @param $key_type string The license key type being produced
  		* @return string Returns the begining string
		**/
		function getBegin($key_type)
		{
			switch($key_type)
			{
				case 'KEY' :
					return $this->_BEGIN1;
				case 'REQUESTKEY' :
					return $this->_BEGIN2;
				default :
					return '';
			}
		}
		
		/**
		* getEnd
		*
		* gets the ending license key seperator text
		*
		* @access private 
		* @param $key_type string The license key type being produced
  		* @return string Returns the ending string
		**/
		function getEnd($key_type)
		{
			switch($key_type)
			{
				case 'KEY' :
					return $this->_END1;
				case 'REQUESTKEY' :
					return $this->_END2;
				default :
					return '';
			}
		}
		
		/**
		* encrypt
		*
		* encrypts the key
		*
		* @access private 
		* @param $src_array array The data array that contains the key data
  		* @return string Returns the encrypted string
		**/
		function encrypt($src_array, $key_type='KEY')
		{
			# get the key
			$key 	= $this->getKey($key_type);
			
			# check to see if mycrypt exists
			if($this->_USE_MCRYPT)
			{
				# openup mcrypt
				$td 	= mcrypt_module_open($this->_ALGORITHM, '', 'ecb', '');
				$iv 	= mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
				# process the key
				$key 	= substr($key, 0, mcrypt_enc_get_key_size($td));
				# init mcrypt
				mcrypt_generic_init($td, $key, $iv);
				
				# encrypt data
				# double base64 gets makes all the characters alpha numeric 
				# and gets rig of the special characters
				$crypt 	= base64_encode(base64_encode(trim(mcrypt_generic($td, serialize($src_array)))));
			
				# shutdown mcrypt
				mcrypt_generic_deinit($td);
				mcrypt_module_close($td);
			}
			else
			{
				# if mcrypt doesn't exist use regular encryption method
				# init the vars
				$crypt = '';
				$str = serialize($src_array);
				
				# loop through the str and encrypt it
				for($i=1; $i<=strlen($str); $i++)
				{
					$char 		= substr($str, $i-1, 1);
					$keychar 	= substr($key, ($i % strlen($key))-1, 1);
					$char 		= chr(ord($char)+ord($keychar));
					$crypt		.= $char;
				}
				
				# base64 the string so it is freindly
				$crypt = base64_encode(base64_encode(trim($crypt)));
			}

			# return the key
			return $crypt;
		}
		
		/**
		* decrypt
		*
		* decrypts the key
		*
		* @access private 
		* @param $enc_string string The key string that contains the data
  		* @return array Returns decrypted array
		**/
		function decrypt($str, $key_type='KEY')
		{
			# get the key
			$key 	= $this->getKey($key_type);
			
			# check to see if mycrypt exists
			if($this->_USE_MCRYPT)
			{
				# openup mcrypt
				$td 	= mcrypt_module_open($this->_ALGORITHM, '', 'ecb', '');
				$iv 	= mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
				# process the key
				$key 	= substr($key, 0, mcrypt_enc_get_key_size($td));
				# init mcrypt
				mcrypt_generic_init($td, $key, $iv);
	
				# decrypt the data and return
				$decrypt = unserialize(mdecrypt_generic($td, base64_decode(base64_decode($str))));
	
				# shutdown mcrypt
				mcrypt_generic_deinit($td);
				mcrypt_module_close($td);
			}
			else
			{
				# if mcrypt doesn't exist use regular decryption method
				# init the decrypt vars
				$decrypt 	= '';
				$str 		= base64_decode(base64_decode($str));
				
				# loop through the text and decode the string
				for($i=1; $i<=strlen($str); $i++)
				{
					$char 		= substr($str, $i-1, 1);
					$keychar 	= substr($key, ($i % strlen($key))-1, 1);
					$char 		= chr(ord($char)-ord($keychar));
					$decrypt   .= $char;
				}
				# turn the string back into an array
				$decrypt = unserialize($decrypt);
			}
			# return the key
			return $decrypt;
		}
		
		/**
		* wrapLicense
		*
		* wraps up the license key in a nice little package
		*
		* @access private 
		* @param $src_array array The array that needs to be turned into a license str
		* @param $key_type string The type of key to be wrapped (KEY=license key, REQUESTKEY=license request key)
  		* @return string Returns encrypted and formatted license key
		**/
		function wrapLicense($src_array, $key_type='KEY')
		{
			# sort the variables
			$begin 	= $this->pad($this->getBegin($key_type));
			$end 	= $this->pad($this->getEnd($key_type));
			
			# encrypt the data
			$str 	= $this->encrypt($src_array, $key_type);
			
			# return the wrap
			return $begin.$this->_LINEBREAK.wordwrap($str, $this->_WRAPTO, $this->_LINEBREAK, 1).$this->_LINEBREAK.$end;
		}
		
		/**
		* unwrapLicense
		*
		* unwraps license key back into it's data array
		*
		* @access private 
		* @param $enc_str string The encrypted license key string that needs to be decrypted
		* @param $key_type string The type of key to be unwrapped (KEY=license key, REQUESTKEY=license request key)
  		* @return array Returns license data array
		**/
		function unwrapLicense($enc_str, $key_type='KEY')
		{
			# sort the variables
			$begin 	= $this->pad($this->getBegin($key_type));
			$end 	= $this->pad($this->getEnd($key_type));
			
			# get string without seperators
			$str 	= trim(str_replace(array($begin, $end, "\r", "\n", "\t"), '', $enc_str));

			# decrypt and return the key
			return $this->decrypt($str, $key_type);
		}
		
		/**
		* get_mac_address
		*
		* Used to get the MAC address of the host server. It works with Linux,
		* Darwin (Mac OS X), and Win XP. It may work with others as some other
		* os's have similar ifconfigs to Darwin but they haven't been tested
		*
		* @access private 
  		* @return string Mac address if found
  		* @return string ERROR_OPEN means config can't be found and thus not opened
  		* @return string MAC_404 means mac adress doesn't exist in the config file
  		* @return string SAFE_MODE means server is in safe mode so config can't be read
		**/
		function get_mac_address()
		{
			if(ini_get('safe_mode'))
			{
				# returns invalid because server is in safe mode thus not allowing 
				# sbin reads but will still allow it to open. a bit weird that one.
				return 'SAFE_MODE';
			}
			
			# if anyone has any clues for windows environments
			# or other server types let me know
			$os = strtolower(PHP_OS);
			if(substr($os, 0, 3)=='win')
			{
				# this windows version works on xp running apache 
				# based server. it has not been tested with anything
				# else, however it should work with NT, and 2000 also
				
				# execute the ipconfig
				exec('ipconfig/all', $lines);
				# seperate the lines
				foreach ($lines as $key=>$line)
				{
					# check for the mac signature in the line
					if(strpos(strtolower($line), 'physical address'))
					{
						$trimmed_line = trim($line);
						# take of the mac addres and return
						return trim(substr($trimmed_line, strrpos($trimmed_line, " ")));
					}
				}
			}
			else
			{
				# switch between the os's
				switch($os)
				{
					# not sure if the string is correct for FreeBSD
					# not tested
					case 'freebsd' : 
					# not sure if the string is correct for NetBSD
					# not tested
					case 'netbsd' : 
					# not sure if the string is correct for Solaris
					# not tested
					case 'solaris' : 
					# not sure if the string is correct for SunOS
					# not tested
					case 'sunos' : 
					# darwin is mac os x
					# tested only on the client os
					case 'darwin' : 
						$os_var = 'ether';
						$os_file = '/sbin/ifconfig';
						break;
					# linux variation
					# tested on server
					case 'linux' : 
						$os_var = 'HWaddr';
						$os_file = '/sbin/ifconfig';
						break;
					default : 
						break;
				}
				
				# open the ipconfig
				$fp = @popen($os_file, "rb");
				if (!$fp)
				{
					# returns invalid, cannot open ifconfig
					return 'ERROR_OPEN';
				}
				
				# read the config
				$conf = @fread($fp, 4096);
				@pclose($fp);
				
				# get the pos of the os_var to look for
				$pos = strpos($conf, $os_var);
				if($pos)
				{
					# seperate out the mac address
					$str1 = trim(substr($conf, ($pos+strlen($os_var))));
					return trim(substr($str1, 0, strpos($str1, "\n")));
				}
			}
			# failed to find the mac address
			return 'MAC_404'; 
		}

		/**
		* getServerInfo
		*
		* used to generate the server binds when server binding is needed.
		*
		* @access private 
  		* @return array server bindings
  		* @return boolean false means that the number of bindings failed to 
  		*		  meet the required number
		**/
		function getServerInfo()
		{
			if(empty($this->_SERVER_VARS))
			{
				$this->setServerVars($_SERVER);
			}
			# get the server specific uris
			$a = array();
			if(isset($this->_SERVER_VARS['SERVER_ADDR']) && (!strrpos($this->_SERVER_VARS['SERVER_ADDR'], '127.0.0.1') || $this->_ALLOW_LOCAL))
			{
				$a['SERVER_ADDR'] = $this->_SERVER_INFO['SERVER_ADDR'];
			}
			if(isset($this->_SERVER_VARS['HTTP_HOST']) && (!strrpos($this->_SERVER_VARS['SERVER_ADDR'], '127.0.0.1') || $this->_ALLOW_LOCAL))
			{
				$a['HTTP_HOST'] =  $this->_SERVER_VARS['HTTP_HOST'];
			}
			if(isset($this->_SERVER_VARS['SERVER_NAME']))
			{
				$a['SERVER_NAME'] =  $this->_SERVER_VARS['SERVER_NAME'];
			}
			if(isset($this->_SERVER_VARS['PATH_TRANSLATED']))
			{
				$a['PATH_TRANSLATED'] = substr($this->_SERVER_VARS['PATH_TRANSLATED'], 0, strrpos($this->_SERVER_VARS['PATH_TRANSLATED'], '/'));
			}
			else if(isset($this->_SERVER_VARS['SCRIPT_FILENAME']))
			{
				$a['SCRIPT_FILENAME'] =  substr($this->_SERVER_VARS['SCRIPT_FILENAME'], 0, strrpos($this->_SERVER_VARS['SCRIPT_FILENAME'], '/'));
			}
			if(isset($_SERVER['SCRIPT_URI']))
			{
				$a['SCRIPT_URI'] =  substr($this->_SERVER_VARS['SCRIPT_URI'], 0, strrpos($this->_SERVER_VARS['SCRIPT_URI'], '/'));
			}
			
			# if the number of different uris is less than the required amount,
			# fail the request
			if(count($a) < $this->_REQUIRED_URIS)
			{
				return 'SERVER_FAILED';
			}
			
			return $a;

		}
		
		/**
		* setServerVars
		*
		* to protect against spoofing you should copy the $_SERVER vars into a
		* seperate array right at the first line of your script so parameters can't 
		* be changed in unencoded php files. This doesn't have to be set. If it is
		* not set then the $_SERVER is copied when getServerInfo (private) function
		* is called.
		*
		* @access public 
		* @param $array array The copied $_SERVER array
		**/
		function setServerVars($array)
		{
			$this->_SERVER_VARS = $array;
		}
		
		/**
		* generateRequest
		*
		* generates a request for a key that is sent yo your server for processing
		*
		* @access public 
		* @param $license_span number The number of seconds that the license requested
		*		 will be valid for. (default 2 weeks)
		* @param $other_array array An array that can contain any other data you
		*		 want to store in the key request
  		* @return string key string
  		* @return boolean false means that the key request generation failed
		**/
		function generateRequest($license_start='NOW', $license_span=1209600, $other_array=array())
		{
			if($this->_SERVER_INFO !== false || !$this->_USE_SERVER)
			{
				# set the id
				$DATA['_ID'] 				= md5($this->_ID2);
				
				# time bing the request key if needed
				if($this->_USE_TIME_REQUEST)
				{
					$current 					= time();
					# life of the request
					$DATA['DATE']['START'] 		= $current;
					$DATA['DATE']['SPAN'] 		= 172800; # request key will be valid for 48 hours
					$DATA['DATE']['END'] 		= $current+172800;
				}
				
				# set time binds
				if($this->_USE_TIME)
				{
					$current 								= time();
					# set the license start time
					$DATA['_DETAILS']['_DATE']['_START'] 	= ($start=='NOW') ? $current : (($current < $license_start) ? $license_start : $current+$license_start);
					# set the license span, note this is not the request span but the actuall license span
					$DATA['_DETAILS']['_DATE']['_SPAN'] 	= $license_span;
				}
				
				# set server binds
				if($this->_USE_SERVER)
				{
					# set the mac id
					$DATA['_DETAILS']['_MAC'] 	= $this->_MAC;
					
					# set the mac id
					$DATA['_DETAILS']['_SERVER']= $this->getServerInfo();
				}

				# set the server os
				$other_array['_PHP_OS'] = PHP_OS;  
				
				# set the server os
				$other_array['_PHP_VERSION'] = PHP_VERSION;  

				# merge the data with the other array
				$DATA['_DATA'] 	= $other_array;

				# encrypt and return the key
				return $this->wrapLicense($DATA, 'REQUESTKEY');
			}
			# no key can be generated so returns false
			return false;
		}

		/**
		* getRequestData
		*
		* returns the request data as an array without producing a return key
		*
		* @access public 
		* @param $request_str string The request key string
  		* @return array The Request key data
  		* @return boolean false means that the key request generation failed
		**/
		function getRequestData($request_str)
		{
			# decrypt the data
			return $this->validateRequest($request_str, false);
		}
		/**
		* validateRequest
		*
		* validates the key request. 
		*
		* @access public 
		* @param $request_str string The request key string.
		* @param $returnkey boolean Signals to generate a valid key for the request
		* 		 or just return the data.
		* @param $other_array array An array that can contain any other data you
		*		 want to store in the key request
  		* @return array Main object in array is 'RESULT', it contains the result
  		*		 of the validation.
  		*		 OK 		- key is valid
  		*		 CORRUPT 	- key has been tampered with
  		*		 TMINUS 	- the key is being used before the valid start date
  		*		 EXPIRED 	- the key has expired
  		*		 INVALID 	- the the encryption key used to encrypt the key differs or the key is not complete
  		*		 EMPTY	 	- the the key is empty
		**/
		function validateRequest($request_str, $returnkey=true, $other_array=array())
		{
			if(strlen($request_str)>0)
			{
				# decrypt the data
				$DATA										= $this->unwrapLicense($request_str, 'REQUESTKEY');

				if(is_array($DATA))
				{	
					# missing / incorrect id therefore it has been tampered with
					if($DATA['_ID'] != md5($this->_ID2))
					{
						$DATA['RESULT'] = 'CORRUPT';
					}
					if($this->_USE_TIME_REQUEST)
					{
						# validate the key request time
						# the license is being used before it's official start
						if($DATA['DATE']['START'] > time()+$this->_START_DIF)
						{
							$DATA['RESULT'] = 'TMINUS';
						}
						# the license has expired
						if($DATA['DATE']['END']-time() < 0)
						{
							$DATA['RESULT'] = 'EXPIRED';
						}
					}
					if( $returnkey)
					{
						# passed all current test so license is ok
						if(!isset($DATA['RESULT']))
						{
							# init the key array
							$KEY = array();
								
							# set the id
							$KEY['_ID'] 				= md5($this->_ID1);
	
							# set time binds
							if($this->_USE_TIME)
							{
								$current 					= time();
								$start						= ($start=='NOW') ? $current : (($current < $start) ? $start : $current+$start);
								# set the dates
								$KEY['_DATE']['START'] 		= $DATA['_DETAILS']['_DATE']['_START'];
								$KEY['_DATE']['SPAN'] 		= $DATA['_DETAILS']['_DATE']['_SPAN'];
								if($DATA['_DETAILS']['_DATE']['_SPAN'] == 'NEVER')
								{
									$KEY['_DATE']['END'] 		= 'NEVER';
								}
								else
								{
									$KEY['_DATE']['END'] 		= $DATA['_DETAILS']['_DATE']['_START'] + $DATA['_DETAILS']['_DATE']['_SPAN'];
								}
							}
							
							# set the server
							if($this->_USE_SERVER)
							{
								# the server details
								$KEY['_MAC'] 	= $DATA['_DETAILS']['_MAC'];
								$KEY['_SERVER'] = $DATA['_DETAILS']['_SERVER'];
							}
							
							# set the server os
							$other_array['_PHP_OS'] = PHP_OS;  
							
							# set the server os
							$other_array['_PHP_VERSION'] = PHP_VERSION;  

							# merge the other data arrays
							$KEY['_DATA'] 					= array_merge($other_array, $DATA['_DATA']);
							
							$DATA['KEY']	= $this->wrapLicense($KEY);
							$DATA['RESULT'] = 'OK';
						}
						# data is returned for use
						return $DATA;
					}
					else
					{
						$DATA['RESULT'] = 'OK';
						return $DATA;
					}
				}
				else
				{
					# the are two reason that mean a invalid return
					# 1 - the other hash key is different
					# 2 - the key has been tampered with
					return array('RESULT'=>'INVALID'); 
				}
			}
			# returns empty because there is nothing in the dat_string
			return array('RESULT'=>'EMPTY'); 
		}

		/**
		* generate
		*
		* generates the server key when the license class resides on the server
		*
		* @access public 
		* @param $start number/string The number of seconds untill the key is valid
		*		 if the value is 'NOW' then the current value given by time() is 
		*		 used as the start date.
		* @param $expire_in number The number of seconds the key will be valid for
		*		 (the default reverts to 31449600 - 1 year)
		* @param $other_array array An array that can contain any other data you
		*		 want to store in the key
  		* @return string key string
  		* @return boolean false means that the key generation failed
		**/
		function generate($start='NOW', $expire_in=31449600, $other_array=array())
		{
			# if the URIS returned are false it means that there has not been
			# enough unique data returned by the $_SERVER so cannot generate key
			if($this->_SERVER_INFO !== false || !$this->_USE_SERVER)
			{
				# set the id
				$DATA['_ID'] 				= md5($this->_ID1);
				
				# set time binds
				if($this->_USE_TIME && !is_array($start))
				{
					$current 					= time();
					$start						= ($start=='NOW') ? $current : (($current < $start) ? $start : $current+$start);
					# set the dates
					$DATA['_DATE']['START'] 	= $start;
					$DATA['_DATE']['SPAN'] 		= $expire_in;
					if($expire_in == 'NEVER')
					{
						$DATA['_DATE']['END'] 		= 'NEVER';
					}
					else
					{
						$DATA['_DATE']['END'] 		= $start+$expire_in;
					}
				}
				
				# set server binds
				if($this->_USE_SERVER)
				{
					# set the mac id
					$DATA['_MAC'] 				= $this->_MAC;
					
					# set the mac id
					$DATA['_SERVER'] 			= $this->_SERVER_INFO;
				}
				
				# if start is array then it is the other array and time binding is not in use
				# convert to other array
				if(is_array($start))
				{
					$other_array = $start;
				}
				
				# set the server os
				$other_array['_PHP_OS'] 	 = PHP_OS;  
				
				# set the server os
				$other_array['_PHP_VERSION'] = PHP_VERSION;  

				# merge the data with the other array
				$DATA['_DATA']				 = $other_array;

				# encrypt and return the key
				return $this->wrapLicense($DATA);
			}
			# no key can be generated so returns false
			return false;
		}
				
		/**
		* validate
		*
		* validates the server key and returns a data array. 
		*
		* @access public 
		* @param $dat_str string Key string.
  		* @return array Main object in array is 'RESULT', it contains the result
  		*		 of the validation.
  		*		 OK 		- key is valid
  		*		 CORRUPT 	- key has been tampered with
  		*		 TMINUS 	- the key is being used before the valid start date
  		*		 EXPIRED 	- the key has expired
  		*		 ILLEGAL 	- the key is not on the same server the license was registered to
  		*		 INVALID 	- the the encryption key used to encrypt the key differs or the key is not complete
  		*		 EMPTY	 	- the the key is empty
		**/
		function validate($dat_str, $dialhome=false, $dialhost="", $dialpath="", $dialport="80")
		{
			if(strlen($dat_str)>0)
			{
				# decrypt the data
				$DATA										= $this->unwrapLicense($dat_str);
				if(is_array($DATA))
				{	
					# missing / incorrect id therefore it has been tampered with
					if($DATA['_ID'] != md5($this->_ID1))
					{
						$DATA['RESULT'] = 'CORRUPT';
					}
					if($this->_USE_TIME)
					{
						# the license is being used before it's official start
						if($DATA['_DATE']['START'] > time()+$this->_START_DIF)
						{
							$DATA['RESULT'] = 'TMINUS';
						}
						# the license has expired
						if($DATA['_DATE']['END']-time() < 0 && $DATA['_DATE']['SPAN'] != 'NEVER')
						{
							$DATA['RESULT'] = 'EXPIRED';
						}
					}
					if($this->_USE_SERVER)
					{
						# the server details
						if($DATA['_MAC'] != $this->_MAC || count(array_diff($this->_SERVER_INFO, $DATA['_SERVER'])) > 0)
						{
							$DATA['RESULT'] = 'ILLEGAL';
						}
					}
					# passed all current test so license is ok
					if(!isset($DATA['RESULT']))
					{
						# dial to home server if required
						if($dialhome)
						{
							# create the details to send to the home server
							$stuff_to_send = array();
							$stuff_to_send['LICENSE_DATA'] = $DATA;
							$stuff_to_send['LICENSE_DATA']['_KEY'] = md5($dat_str);
							$stuff_to_send['SERVER_DATA']['MAC'] = $this->_MAC;
							$stuff_to_send['SERVER_DATA']['SERVER'] = $this->_SERVER_INFO;
							# dial home
							$DATA['RESULT'] = $this->callHome($stuff_to_send, $dialhost, $dialpath, $dialport);
						}
						else
						{
							# result is ok all test passed, license is legal
							$DATA['RESULT'] = 'OK';
						}
					}
				/*
					*/
					# data is returned for use
					return $DATA;
				}
				else
				{
					# the are two reason that mean a invalid return
					# 1 - the other hash key is different
					# 2 - the key has been tampered with
					return array('RESULT'=>'INVALID'); 
				}
			}
			# returns empty because there is nothing in the dat_string
			return array('RESULT'=>'EMPTY'); 
		}
		
		/**
		* generateReturnData
		*
		* generates an encrypted string used to return data once the license has
		* been validated on the return server
		*
		* @access private 
		* @param $array array Array that contains the return info from the dial home server
  		* @return string encrypted data string containing server validation info
		**/
		function generateReturnData($array)
		{
			return $this->_BEGIN3.$this->encrypt($array, 'HOMEKEY').$this->_END3;
		}
		
		/**
		* postData
		*
		* Posts data to and recieves data from dial home server. Returned info
		* contains the dial home validation result
		*
		* @access private 
		* @param $host string Host name of the server to be contacted
		* @param $path string Path of the script for the data to be sent to
		* @param $query_array array Array that contains the license key info to be validated
		* @param $port number Port Number to send the data through
  		* @return array Result of the dialhome validation
  		* @return string SOCKET_FAILED will be returned if it was not possible to open a socket to the home server
		**/
		function postData($host, $path, $query_array, $port=80)
		{
			# genereate the post query info
			$query 	 = 'POSTDATA='.$this->encrypt($query_array, 'HOMEKEY');
			$query 	 .= '&MCRYPT='.$this->_USE_MCRYPT;
			# init the return string
			$return  = '';
			
			# generate the post headers
			$post  	 = "POST $path HTTP/1.1\r\n";
			$post 	.= "Host: $host\r\n";
			$post 	.= "Content-type: application/x-www-form-urlencoded\r\n";
			$post 	.= "Content-length: ".strlen($query)."\r\n";
			$post 	.= "Connection: close\r\n";
			$post 	.= "\r\n";
			$post 	.= $query;

			# open a socket
			$header = @fsockopen($host, $port);
			if(!$header)
			{
				# if the socket fails return failed
				return array('RESULT'=>'SOCKET_FAILED');
			}
			@fputs($header, $post);
			# read the returned data
			while (!@feof($header))
			{
				$return .= @fgets($header, 1024);
			}
			fclose($header);
			# trace($return);
			# seperate out the data using the delims
			$leftpos = strpos($return, $this->_BEGIN3)+strlen($this->_BEGIN3);
			$rightpos = strpos($return, $this->_END3)-$leftpos;

			# decrypt and return the data
			return $this->decrypt(substr($return, $leftpos, $rightpos), 'HOMEKEY');
		}

		/**
		* callHome
		*
		* calls the dial home server (your server) andvalidates the clients license
		* with the info in the mysql db
		*
		* @access private 
		* @param $data array Array that contains the info to be validated
		* @param $dialhost string Host name of the server to be contacted
		* @param $dialpath string Path of the script for the data to be sent to
		* @param $dialport number Port Number to send the data through
  		* @return string Returns the encrypted server validation result from the dial home call
		**/
		function callHome($data, $dialhost, $dialpath, $dialport)
		{
			# post the data home
			$data = $this->postData($dialhost, $dialpath, $data, $dialport);
			return (empty($data['RESULT'])) ? 'SOCKET_FAILED' : $data['RESULT'];
		}
		
		/**
		* recieveCall
		*
		* the function to be called by the script that recieves the call on your
		* server from the client server
		*
		* @access public 
		* @param $data array Data array (ie $_POST['POSTDATA']) recieved from
		*					 the client dialing home
		* @param $table string Database table that contains the distributionLicense sql
		* @param $user string User for the mysql db
		* @param $pass string Pass for the mysql db
		* @param $host string Host for the mysql db
  		* @return string Returns the encrypted server validation result from the dial home call
  		*			EMPTY_DATA					- no data has been given to the recieveCall function
  		*			EMPTY_ID					- no id has been found in the data sent so dial home check cannot be made
  		*			SERVER_LICENSE_404			- the license does not exist on the dial home server
  		*			SERVER_LICENSE_MISMATCH		- the license key sent to the home server and the license key residing in the 
  		*										  home server db do not match
  		*			SERVER_LICENSE_INACTIVE		- license is inactive (ie expired)
  		*			SERVER_LICENSE_WARNING		- a warning has been given to the license holder
  		*			SERVER_LICENSE_SUSPENDED	- the license has been suspended
  		*			SERVER_LICENSE_REVOKED		- license has been revoked
  		*			SERVER_PAYMENT_NOT_MADE		- license has not been paid for
  		*			SERVER_PAYMENT_DUE			- next payment is due
  		*			SERVER_DATE_MISMATCH		- license start date does not match the one on the dial home server
  		*			SERVER_MAC_MISMATCH			- the mac address registered with the license does not match the one on the home server
  		*			SERVER_SERVER_MISMATCH		- the server vars supplied in the license mismatch the stored vars on the home server
  		*			OK							- the license correspods to the info held on the home server and all is ok
		**/
		function recieveCall($data, $table, $user, $pass, $host='localhost')
		{
			# if there is not data die with an error
			if(empty($data))
			{
				return $this->generateReturnData(array('RESULT'=>'EMPTY_DATA'));
			}
			# init the results array
			$results = array();
			# decrypt the data
			$data = $this->decrypt($data, 'HOMEKEY');
			# if there is no license id to check then return with error
			if(!isset($data['LICENSE_DATA']['_DATA']['_LICENSE_ID']))
			{
				return $this->generateReturnData(array('RESULT'=>'EMPTY_ID'));
			}

			# connect to the db
			$this->connect($table, $user, $pass, $host);

			# insert log remote log data
			mysql_query('INSERT INTO `'.$this->_DB_PREFIX.'remote_log` (`ID`,`TIMESTAMP`,`LICENSE_ID`,`MESSAGE`,`SERVER`,`IP`) VALUES (NULL,"'.time().'","'.$data['LICENSE_DATA']['_DATA']['_LICENSE_ID'].'","'.'test'.'","'.urlencode(serialize($data['_SERVER'])).'","'.$_SERVER['REMOTE_ADDR'].'")', $this->_DB);

			# check up on license in the license table
			$lookup_result = mysql_query('SELECT * FROM `'.$this->_DB_PREFIX.'licenses` WHERE `LICENSE_ID`="'.$data['LICENSE_DATA']['_DATA']['_LICENSE_ID'].'"', $this->_DB);
 			# get the license data from the server
			$license = mysql_fetch_array($lookup_result, MYSQL_ASSOC);
			
			# disconnect from the db
			$this->disconnect();

			# if the result is not returned or the license does not exist			
			if(!$lookup_result || !$license)
			{
				# die and send back the result to the server calling home
				return $this->generateReturnData(array('RESULT'=>'SERVER_LICENSE_404'));
			}

			# compare the license sent against the license stored
			# if any changes have been made to the clients license without updating the one
			# on the server then this will throw an error, main cause would be a hack or
			# developer error
			if($license['LICENSE_KEY'] != $data['LICENSE_DATA']['_KEY'])
			{
				return $this->generateReturnData(array('RESULT'=>'SERVER_LICENSE_MISMATCH'));
			}
			
			# check if license is active
			switch($license['STATUS'])
			{
				case 0 :
					return $this->generateReturnData(array('RESULT'=>'SERVER_LICENSE_INACTIVE'));
				case -1 :
					return $this->generateReturnData(array('RESULT'=>'SERVER_LICENSE_WARNING'));
				case -2 :
					return $this->generateReturnData(array('RESULT'=>'SERVER_LICENSE_SUSPENDED'));
				case -3 :
					return $this->generateReturnData(array('RESULT'=>'SERVER_LICENSE_REVOKED'));
				default :
					break;
			}

			# check if user has paid
			if(!$license['PAID'])
			{
				return $this->generateReturnData(array('RESULT'=>'SERVER_PAYMENT_NOT_MADE'));
			}

			# check if the next pay date is passed
			if($license['PAYMENT_NEXT_DUE']>0 && $license['PAYMENT_NEXT_DUE']  < time())
			{
				return $this->generateReturnData(array('RESULT'=>'SERVER_PAYMENT_DUE'));
			}

			# check the start date and see if it is valid
			if($this->_USE_TIME && ($license['START_DATE'] != $data['LICENSE_DATA']['_DATE']['START'] || $license['EXPIRY_DATE'] != $data['LICENSE_DATA']['_DATE']['END']))
			{
				return $this->generateReturnData(array('RESULT'=>'SERVER_DATE_MISMATCH'));
			}

			# if server checks are required
			if($this->_USE_SERVER)
			{
				# check mac address
				if($license['MAC'] != $data['LICENSE_DATA']['_MAC'])
				{
					return $this->generateReturnData(array('RESULT'=>'SERVER_MAC_MISMATCH'));
				}

				# check the server vars
				$server_difs = count(array_diff(unserialize(urldecode($license['SERVER'])), $data['LICENSE_DATA']['_SERVER']));
				if($server_difs > 0)
				{
					return $this->generateReturnData(array('RESULT'=>'SERVER_SERVER_MISMATCH'));
				}
			}
			
			return $this->generateReturnData(array('RESULT'=>'OK'));
		}
		
		/**
		* connect
		*
		* connects to the mysql db holding the license data
		*
		* @access private 
		* @param $table string Database table that contains the distributionLicense sql
		* @param $user string User for the mysql db
		* @param $pass string Pass for the mysql db
		* @param $host string Host for the mysql db
  		* @return resource id
		**/
		function connect($table, $user, $pass, $host)
		{
			# connect to your servers DB
			$this->_DB = mysql_pconnect($host, $user, $pass);
			# select the table required
			mysql_select_db($table, $this->_DB);
			return $this->_DB;
		}
		
		/**
		* disconnect
		*
		* disconnects the mysql db
		* been validated on the return server
		*
		* @access private 
  		* @return boolean 
		**/
		function disconnect()
		{
			# close the database
			return mysql_close($this->_DB);
		}
		
	}

?>

Comment and Contribute

Your comment has been submitted and is pending approval.

Author:
Oliver Lillie

Comment:



Comment:

(Maximum characters: 1200). You have characters left.