Login   Register  
PHP Classes
elePHPant
Icontem

File: server/class.license.server.php

Recommend this page to a friend!
Stumble It! Stumble It! Bookmark in del.icio.us Bookmark in del.icio.us
  Classes of Oliver Lillie  >  PADL (PHP Application Distribution License System)  >  server/class.license.server.php  >  Download  
File: server/class.license.server.php
Role: Class source
Content type: text/plain
Description: Subclass Source
Class: PADL (PHP Application Distribution License System)
Generate PHP application license keys
Author: By
Last change: Important Security and Consistency updates
Date: 2005-06-20 15:05
Size: 23,305 bytes
 

Contents

Class file image Download
<?php

	/**
	* Project:		Distrubution License Class
	* File:			class.license.server.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.1
	* @history---------------------------------------------
	* see CHANGELOG
	*/
	
	class license_server extends padl {
	
		/**
		* init the database value.
		*/
		var $_DB;

		/**
		* the database table prefix.
		*
		* @var array
		*/
		var $DB_PREFIX			= '';
		
		/**
		* the default license approval type. 
		*			- ADMIN means the key generation needs admin approval
		*			- AUTO means the key generation is done on request
		*
		* @var string
		*/
		var $APPROVAL_TYPE		= 'ADMIN';
		
		/**
		* init the approval type vars.
		*/
		var $APPROVAL_DATA;
		var $APPROVAL_TIME;

		/**
		* 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 license_server($use_mcrypt=true, $use_time=true, $use_server=true, $allow_local=false, $approval_type=false)
		{
			# init the class
			$this->init($use_mcrypt, $use_time, $use_server, $allow_local);
			# set the registration approval type
			if(is_string($approval_type))
			{
				$this->set_approval_type($approval_type);
			}
		}
		
		/**
		* set_approval_type
		*
		* sets the approval type
		*
		* @access public 
		* @param $type string The type of license generation
		*		 ADMIN 	- generates a temp key and assigns the $data param into the key.
		*				  the key will be valid for the period set by the validty param.
		*							  NOTE: if validity is set to 0 then a key is not returned to the client
		*		AUTO	- generates the key when one is asked for. the data array is used, however
		*				  the validity period is treated as a cap for the time span sent from the client
		*				  install, thus if the validy is less than the asked for time period it will override
		* @param $data array An array of data to be bound into the generated key
		* @param $validity number If used with ADMIN it is the length of the temporary key generated. if set to 0
		*				  then no temp key is generated, however if used with AUTO it caps the requested licenses
		*				  time span. If no time span is requested by the client and this is not set to 0 then a span
		*				  will be placed into the key. This way you can issue licenses and not worry about the client
		*				  trying to hack the timespan of the license.
  		* @return resource id
		**/
		function set_approval_type($type='ADMIN', $data=array(), $validity=0)
		{
			$this->APPROVAL_TYPE = $type;
			$this->APPROVAL_DATA = $data;
			$this->APPROVAL_TIME = $validity;
		}

		/**
		* connect
		*
		* connects to the mysql db holding the license data
		*
		* @access public 
		* @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='localhost')
		{
			# 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 public 
  		* @return boolean 
		**/
		function disconnect()
		{
			# close the database
			return @mysql_close($this->_DB);
		}
		
		/**
		* _query
		*
		* builds, then excecutes then returns data for mysql queries 
		*
		* @access private 
		* @param $table string The table to build the query for
		* @param $action string The type of action to build the query for
		* @param $data array The data array containing arguments for the query building
  		* @return array 
		**/
		function _query($table, $action, $data=array())
		{
			$query =  $this->_build_query($table, $action, $data);
			return $this->_execute_query($query);
		}
		
		/**
		* _execute_query
		*
		* excecutes a mysql query and returns the results
		*
		* @access private 
		* @param $query string The query string to execute
  		* @return array 
		**/
		function _execute_query($query)
		{
			# determine the query type
			$query_type = strpos($query, 'INSERT')===false ? (strpos($query, 'SELECT')===false ? 'other' : 'return') : 'insert';
			# run the query
			$query_result = @mysql_query($query, $this->_DB);
			if(!$query_result) return array('RESULT'=>false, 'SQL'=>$query, 'ERROR'=>mysql_error());
			# switch through the query types in order to return the correct data
			switch($query_type)
			{
				case 'insert' :
					$result = mysql_insert_id($this->_DB);
					break;
				case 'return' :
					$result = array();
					if(empty($data['FETCHTYPE']))
					{
						while($row = mysql_fetch_assoc($query_result))
						{
							$result[] = $row;
						}
					}
					break;
				default :
					$result = true;
					break;
			}
			return array('RESULT'=>$result);
		}
		
		/**
		* _build_query
		*
		* gets the query for the table and qction
		*
		* @access private 
		* @param $table string The table to build the query for
		* @param $action string The type of action to build the query for
		* @param $data array The data array containing arguments for the query building
  		* @return string 
		**/
		function _build_query($table, $action, $data=array())
		{
			# get the table prefix and all
			$table_pre = '`'.$this->DB_PREFIX.$table.'`';
			# switch through the queries
			# and build them
			switch($action)
			{
				case 'insert' :
					switch($table)
					{
						case 'licenses' : 
							return 'INSERT INTO '.$table_pre.' (`LICENSE_ID`,`USER_ID`,`FLAGGED`,`STATUS`,`START_DATE`,`DATE_SPAN`,`EXPIRY_DATE`,`NOTES`,`COST`,`PAID`,`REQUEST_KEY`,`TEMP_KEY`,`LICENSE_KEY`,`PAYMENT_NEXT_DUE`,`DATA`,`MAC`,`PATH`,`IP`) VALUES ('.$this->_get_value($data['LICENSE_ID']).','.$this->_get_value($data['USER_ID']).','.$this->_get_value($data['FLAGGED']).','.$this->_get_value($data['STATUS']).','.$this->_get_value($data['START_DATE']).','.$this->_get_value($data['DATE_SPAN']).','.$this->_get_value($data['EXPIRY_DATE']).','.$this->_get_value($data['NOTES']).','.$this->_get_value($data['COST']).','.$this->_get_value($data['PAID']).','.$this->_get_value($data['REQUEST_KEY']).','.$this->_get_value($data['TEMP_KEY']).','.$this->_get_value($data['LICENSE_KEY']).','.$this->_get_value($data['PAYMENT_NEXT_DUE']).','.$this->_get_value($data['DATA']).','.$this->_get_value($data['MAC']).','.$this->_get_value($data['PATH']).','.$this->_get_value($data['IP']).');';
						case 'remote_log' :
							return 'INSERT INTO '.$table_pre.' (`ID`,`TIMESTAMP`,`LICENSE_ID`,`MESSAGE`,`SERVER`,`IP`) VALUES ('.$this->_get_value($data['ID']).','.$this->_get_value($data['TIMESTAMP']).','.$this->_get_value($data['LICENSE_ID']).','.$this->_get_value($data['MESSAGE']).','.$this->_get_value($data['SERVER']).','.$this->_get_value($data['IP']).');';
					}
				case 'return' :
					return 'SELECT '.$this->_get_value($data['RETURN'], '*', false).' FROM '.$table_pre.' '.$this->_build_matches($data['PAIRS']);
				case 'update' :
					return 'UPDATE '.$table_pre.' '.$this->_build_matches($data['SET'], 'SET', ', ').' '.$this->_build_matches($data['PAIRS']);
				case 'delete' :
					return 'DELETE FROM '.$table_pre.' '.$this->_build_matches($data['PAIRS']);
			}
		}
		
		/**
		* _get_value
		*
		* returns a null string if no value is found
		*
		* @access private 
		* @param $value string A value to be checked
		* @param $fill string The return value if the value is empty
  		* @return string 
		**/
		function _get_value($value, $fill='NULL', $add_quotes=true)
		{
			return empty($value) ? $fill : (is_string($value) && $add_quotes ? '"'.$value.'"' : $value);
		}
		
		/**
		* get_null
		*
		* returns formatted sql, ie `FIELD`="VALUE"
		*
		* @access private 
		* @param $pairs array An arrary containing the field/value pairs
		* @param $pre string The matches preceding command
		* @param $sep string The matches seperator
  		* @return string 
		**/
		function _build_matches($pairs, $pre='WHERE', $sep=' && ')
		{
			$num = count($pairs);
			# if there are no pairs return and empty string
			if($num == 0) return '';
			# init the string
			$str = $pre." ";
			# loop through the pairs and build the sql
			for($i=0; $i<$num; $i++)
			{
				$pair 		= $pairs[$i];
				$value 		= $pair['VALUE'];
				$comparison = empty($pair['COMP']) ? '=' : $pair['COMP'];
				$str 	.= '`'.$pair['FIELD'].'`'.$comparison.(is_string($value) ? '"'.$value.'"' : $value);
				if($i < $num-1)
				{
					$str.= $sep;
				}
			}
			return $str;
		}
		
		/**
		* _generate_return_data
		*
		* 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 _generate_return_data($array)
		{
			return $this->BEGIN2.$this->_encrypt($array, 'HOMEKEY').$this->END2;
		}
		
		/**
		* receiveInstall
		*
		* receives an install and license key request. It validates the install
		* request, process it and checks for duplicates against the allow install
		* number, then returns with the result
		*
		* @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 result from the register_install function in the app
  		*			EMPTY_DATA					- no data has been given to the _recieve_call function
  		*			OVER_INSTALLED				- if the number of installs registered is too many then it is returned
  		*			CORRUPT						- the request does not contain an id and is therefore a corrupt / hacked request
  		*			PENDING						- the data is ok, however the approval type for license generation is set to ADMIN
  		*										  thus any key generation needs to be approved by an administrator
  		*			OK							- the license correspods to the info held on the home server and all is ok
		**/
		function receive_install($data, $table, $user, $pass, $host='localhost')
		{
			$orig_data = $data;
			# init the return array
			$return = array();
			
			# if there is not data die with an error
			if(empty($data)) return $this->_generate_return_data(array('RESULT'=>'EMPTY_DATA'));
			
			# decrypt the data
			$data = $this->_decrypt($data, 'HOMEKEY');
			
			# check for an id
			if($data['ID'] != md5($this->ID2)) return $this->_generate_return_data(array('RESULT'=>'CORRUPT'));
			
			# init the results array
			$results = array();
			# init the license array
			$license = array();

			# insert the id
			$license['ID'] = md5($this->ID1);
			
			# if server checks are required
			if($this->USE_SERVER)
			{
				if(!$this->_compare_domain_ip($data['SERVER']['DOMAIN'], $data['SERVER']['IP'])) $return['RESULT'] = 'DOMAIN_IP_FAIL';
				# update mac address
				$license['SERVER']['DOMAIN']= $data['SERVER']['DOMAIN'];
				# update mac address
				$license['SERVER']['MAC'] 	= $data['SERVER']['MAC'];
				# update the server details
				$license['SERVER']['PATH'] 	= $data['SERVER']['PATH'];
				# update the ip details
				$license['SERVER']['IP'] 	= $data['SERVER']['IP'];
			}
			
			# if time checks are required
			if($this->USE_TIME)
			{
				$license['DATE']['START'] 	= $data['DATE']['START'];
				$license['DATE']['SPAN'] 	= $data['DATE']['SPAN'];
				$license['DATE']['END'] 	= $data['DATE']['END'];
			}
			
			# connect to the db
			$this->connect($table, $user, $pass, $host);

			if(!empty($data['KEY_CODE']))
			{
				# will build in pre approved key codes, thus allowing someone to purchase
				# a license to get a key code. This key code is linked to an individual
				# licensee. If the license key has not been used already and is valid 
				# the license will be generated straight away, regardless of the generation
				# approval variables. It will generate will the the purchase settings from 
				# the database
			}

			# merge the exiting data set in the request with the approval data
			$license['DATA'] = array_merge($data['DATA'], $this->APPROVAL_DATA);
					
			if(!isset($return['RESULT'])) 
			{
				# start building the insertion query
				$query = 'INSERT INTO `'.$this->DB_PREFIX.'licenses` (`LICENSE_ID`,`USER_ID`,`FLAGGED`,`STATUS`,`START_DATE`,`DATE_SPAN`,`EXPIRY_DATE`,`NOTES`,`COST`,`PAID`,`REQUEST_KEY`,`TEMP_KEY`,`LICENSE_KEY`,`PAYMENT_NEXT_DUE`,`INSTALL`,`PACKAGES`,`MAC`,`PATH`,`IP`) VALUES (-VALUES-);';
				
				# sort the approval type
				if($this->APPROVAL_TYPE == 'AUTO')
				{
					# if the approval time is greater than 0 then override the end time
					if($this->APPROVAL_TIME > 0)
					{
						# set the time span of the temp key
						$start 						= time();
						$license['DATE']['START'] 	= $start;
						$license['DATE']['SPAN'] 	= $this->APPROVAL_TIME;
						$license['DATE']['END'] 	= $start+$this->APPROVAL_TIME;
					}
					$create_license = true;
					$return['RESULT'] 	= 'OK';
					$values = 'NULL,NULL,NULL,1,'.$license['DATE']['START'].','.$license['DATE']['SPAN'].','.$license['DATE']['END'].',NULL,NULL,1,"'.$orig_data.'",NULL,NULL,NULL,NULL,NULL,"'.$data['SERVER']['MAC'].'","'.urlencode(serialize($data['SERVER']['PATH'])).'","'.urlencode(serialize($data['SERVER']['IP'])).'"';
				}
				else if($this->APPROVAL_TYPE == 'ADMIN')
				{
					$key = '';
					# if the approval time is greater than 0 generate a temp key
					if($this->APPROVAL_TIME > 0)
					{
						# set the time span of the temp key
						$start 						= time();
						$license['DATE']['START'] 	= $start;
						$license['DATE']['SPAN'] 	= $this->APPROVAL_TIME;
						$license['DATE']['END'] 	= $start+$this->APPROVAL_TIME;
						$license['TEMP'] = 1;
						$create_license = true;
					}
					$values = 'NULL,NULL,NULL,1,'.$license['DATE']['START'].','.$license['DATE']['SPAN'].','.$license['DATE']['END'].',NULL,NULL,NULL,"'.$orig_data.'",NULL,NULL,NULL,NULL,NULL,"'.$data['SERVER']['MAC'].'","'.urlencode(serialize($data['SERVER']['PATH'])).'","'.urlencode(serialize($data['SERVER']['IP'])).'"';
					$return['RESULT'] 	= 'PENDING';
				}
				$query = str_replace('-VALUES-', $values, $query);
				if(!mysql_query($query, $this->_DB))
				{
					$return['ERROR'] = mysql_error();
					$return['RESULT'] = 'SERVER_DB_FAILED';
					$create_license = false;
				}
				
				if($create_license)
				{
					$license['DBID'] = mysql_insert_id($this->_DB);
					# create the key
					$key = $this->_create_license($license);
					$return['KEY'] 	= $key;
					$row = ($this->APPROVAL_TYPE == 'ADMIN') ? 'TEMP_KEY' : 'LICENSE_KEY';
					if(!mysql_query('UPDATE `'.$this->DB_PREFIX.'licenses` SET `'.$row.'`="'.$key.'" WHERE `LICENSE_ID`="'.$license['DBID'].'"'))
					{
						$return['ERROR'] = mysql_error();
						$return['RESULT'] = 'SERVER_DB_FAILED';
						unset($return['KEY']);
					}
				}

			}
			
			# disconnect from the db
			$this->disconnect();
			
			# generate the return data
			return $this->_generate_return_data($return);
		}
		
		/**
		* generate
		*
		* generates the client license key
		*
		* @access private 
		* @param $data array The data array containing the binding info
  		* @return string key string
		**/
		function _create_license($data)
		{
			# set the server os
			$data['DATA']['_PHP_OS'] 	 	= PHP_OS;  
			# set the server os
			$data['DATA']['_PHP_VERSION'] 	= PHP_VERSION;  
			# encrypt the key and return
			return $this->_wrap_license($data);
		}

		/**
		* recieve_call
		*
		* 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 _recieve_call 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 recieve_call($data, $table, $user, $pass, $host='localhost')
		{
			# if there is not data die with an error
			if(empty($data))
			{
				return $this->_generate_return_data(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']['DBID']))
			{
				return $this->_generate_return_data(array('RESULT'=>'EMPTY_ID'));
			}
			
			$istemp = (isset($data['LICENSE_DATA']['TEMP']));

			# 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']['DBID'].'","'.'test'.'","'.urlencode(serialize($data['LICENSE_DATA']['SERVER']['PATH'])).'","'.$_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']['DBID'].'"', $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->_generate_return_data(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
			$stored_key = ($istemp) ? $license['TEMP_KEY'] : $license['LICENSE_KEY'];
			if(md5($stored_key) != $data['LICENSE_DATA']['KEY'])
			{
				return $this->_generate_return_data(array('RESULT'=>'SERVER_LICENSE_MISMATCH'));
			}
			
			# check if license is active
			switch($license['STATUS'])
			{
				case 0 :
					return $this->_generate_return_data(array('RESULT'=>'SERVER_LICENSE_INACTIVE'));
				case -1 :
					return $this->_generate_return_data(array('RESULT'=>'SERVER_LICENSE_WARNING'));
				case -2 :
					return $this->_generate_return_data(array('RESULT'=>'SERVER_LICENSE_SUSPENDED'));
				case -3 :
					return $this->_generate_return_data(array('RESULT'=>'SERVER_LICENSE_REVOKED'));
				default :
					break;
			}

			# check if user has paid
			if(!$license['PAID'] && !$istemp)
			{
				return $this->_generate_return_data(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->_generate_return_data(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->_generate_return_data(array('RESULT'=>'SERVER_DATE_MISMATCH'));
			}

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

				# check the server vars
				$server_difs = count(array_diff(unserialize(urldecode($license['PATH'])), $data['LICENSE_DATA']['SERVER']['PATH']));
				if($server_difs > 0)
				{
					return $this->_generate_return_data(array('RESULT'=>'SERVER_SERVER_MISMATCH'));
				}
			}
			
			return $this->_generate_return_data(array('RESULT'=>'OK'));
		}
		
	}

?>