<?php
  /**************************************************************************\
  * eGroupWare API - Crypto                                                  *
  * This file written by Joseph Engo <jengo@phpgroupware.org>                *
  * Handles encrypting strings based on various encryption schemes           *
  * Copyright (C) 2000, 2001 Dan Kuykendall                                  *
  * -------------------------------------------------------------------------*
  * This library is part of the eGroupWare API                               *
  * http://www.egroupware.org/api                                            *
  * -------------------------------------------------------------------------*
  * This library is free software; you can redistribute it and/or modify it  *
  * under the terms of the GNU Lesser General Public License as published by *
  * the Free Software Foundation; either version 2.1 of the License,         *
  * or any later version.                                                    *
  * This library 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 Lesser General Public License for more details.              *
  * You should have received a copy of the GNU Lesser General Public License *
  * along with this library; if not, write to the Free Software Foundation,  *
  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA            *
  \**************************************************************************/

	/* $Id$ */

	class crypto
	{
		var $enabled = False;
		var $debug = False;

		var $mcrypt_version = '';
		var $algo = MCRYPT_TRIPLEDES;
		var $mode = MCRYPT_MODE_CBC;
		var $td = False; /* Handle for mcrypt */
		var $iv = '';
		var $key = '';

		function crypto($vars='')
		{
			if($GLOBALS['egw_info']['flags']['currentapp'] == 'login' ||
				$GLOBALS['egw_info']['flags']['currentapp'] == 'logout' ||
				$GLOBALS['egw_info']['flags']['currentapp'] == 'home'
			)
			{
				$this->debug = False;
			}
			if(is_array($vars))
			{
				$this->init($vars);
			}
		}

		function init($vars)
		{
			/* _debug_array(mcrypt_list_algorithms()); */
			$key = $vars[0];
			$iv  = $vars[1];

			if($GLOBALS['egw_info']['server']['mcrypt_enabled'] && extension_loaded('mcrypt'))
			{
				if($GLOBALS['egw_info']['server']['mcrypt_algo'])
				{
					$this->algo = $GLOBALS['egw_info']['server']['mcrypt_algo'];
				}
				if($GLOBALS['egw_info']['server']['mcrypt_mode'])
				{
					$this->mode = $GLOBALS['egw_info']['server']['mcrypt_mode'];
				}

				if($this->debug)
				{
					echo '<br>crypto: algorithm=' . $this->algo;
					echo '<br>crypto: mode     =' . $this->mode;
				}

				$this->enabled = True;
				$this->mcrypt_version = $GLOBALS['egw_info']['server']['versions']['mcrypt'];
				if($this->mcrypt_version == 'old')
				{
					$this->td = False;
					if(phpversion() > '4.0.2pl1')
					{
						$keysize = mcrypt_get_key_size($this->algo);
						$ivsize  = mcrypt_get_iv_size($this->algo,$this->mode);
					}
					else
					{
						$keysize = 8;
						$ivsize  = 8;
					}
				}
				else
				{
					/* Start up mcrypt */
					$this->td = mcrypt_module_open($this->algo, '', $this->mode, '');

					$ivsize  = mcrypt_enc_get_iv_size($this->td);
					$keysize = mcrypt_enc_get_key_size($this->td);
				}

				/* Hack IV to be the correct size */
				$x = strlen($iv);
					$this->iv = '';
				for($i = 0; $i < $ivsize; $i++)
				{
					$this->iv .= $iv[$i % $x];
				}

				/* Hack Key to be the correct size */
				$x = strlen($key);
					$this->key = '';
				for($i = 0; $i < $keysize; $i++)
				{
					$this->key .= $key[$i % $x];
				}
			}
			else
			{
				/* If mcrypt isn't loaded, key and iv are not needed. */
				if($this->debug)
				{
					echo '<br>crypto: mycrypt unavailable or disabled';
				}
			}
		}

		function cleanup()
		{
			if($this->enabled)
			{
				if($this->mcrypt_version != 'old')
				{
					if(function_exists('mcrypt_generic_deinit'))
					{
						mcrypt_generic_deinit($this->td);
					}
					else
					{
						mcrypt_generic_end($this->td);
					}
				}
			}
		}

		function hex2bin($data)
		{
			$len = strlen($data);
			return pack('H'.$len, $data);
		}

		function encrypt($data)
		{
			if($this->debug)
			{
				echo '<br>' . time() . ' crypto->encrypt() unencrypted data: ---->>>>' . $data . "\n";
			}

			if(@is_array($data) || @is_object($data))
			{
				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->encrypt() found an "' . gettype($data) . '".  Serializing...' . "\n";
				}
				$data = serialize($data);
				$_obj = True;
			}
			else
			{
				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->encrypt() found "' . gettype($data) . '". No serialization...' . "\n";
				}
			}

			/* Disable all encryption if the admin didn't set it up */
			if($this->enabled)
			{
				if($_obj)
				{
					if($this->debug)
					{
						echo '<br>' . time() . ' crypto->encrypt() adding slashes' . "\n";
					}
					$data = addslashes($data);
				}

				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->encrypt() data: ---->>>>' . $data;
				}

				switch($this->mcrypt_version)
				{
					case 'old':
						/* The old code, only works with mcrypt <= 2.2.x */
						$encrypteddata = mcrypt_cbc($this->algo, $this->key, $data, MCRYPT_ENCRYPT);
						break;
					default:
						/* Handle 2.4 and newer API */
						mcrypt_generic_init($this->td, $this->key, $this->iv);
						$encrypteddata = mcrypt_generic($this->td, $data);
						break;
				}
				$encrypteddata = bin2hex($encrypteddata);
				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->encrypt() crypted data: ---->>>>' . $encrypteddata;
				}
				return $encrypteddata;
			}
			else
			{
				/* No mcrypt == insecure ! */
				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->encrypt() crypted data: ---->>>>' . $data;
				}
				return $data;
			}
		}

		function decrypt($encrypteddata)
		{
			if($this->debug)
			{
				echo '<br>' . time() . ' crypto->decrypt() crypted data: ---->>>>' . $encrypteddata;
			}
			/* Disable all encryption if the admin didn't set it up */
			if($this->enabled)
			{
				$data = $this->hex2bin($encrypteddata);
				switch($this->mcrypt_version)
				{
					case 'old':
						/* The old code, only works with mcrypt <= 2.2.x */
						$data = mcrypt_cbc($this->algo, $this->key, $data, MCRYPT_DECRYPT);
						break;
					default:
						/* Handle 2.4 and newer API */
						mcrypt_generic_init($this->td, $this->key, $this->iv);
						$data = mdecrypt_generic($this->td, $data);
						break;
				}

				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->decrypt() decrypted data: ---->>>>' . $data;
				}
				$test = stripslashes($data);
				if(@unserialize($test))
				{
					if($this->debug)
					{
						echo '<br>' . time() . ' crypto->decrypt() stripping slashes' . "\n";
					}
					$data = $test;
				}
				unset($test);

				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->decrypt() data: ---->>>>' . $data . "\n";
				}
			}
			else
			{
				/* No mcrypt == insecure ! */
				$data = $encrypteddata;
			}

			// Fix strange bug
			// Without this, somes ^@^@^@^@ appears in data
			$data = chop($data);

			$newdata = @unserialize($data);
			/* Check whether an array or object exists, even if empty. These should be the only ones originally serialized. */
			if(@is_array($newdata) || @is_object($newdata))
			{
				/* array or object */
				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->decrypt() found serialized "' . gettype($newdata) . '".  Unserializing...' . "\n";
					echo '<br>' . time() . ' crypto->decrypt() returning: '; _debug_array($newdata);
				}
				return $newdata;
			}
			else
			{
				/* Other types */
				if($this->debug)
				{
					echo '<br>' . time() . ' crypto->decrypt() found UNserialized "' . gettype($data) . '".  No unserialization...' . "\n";
					echo '<br>' . time() . ' crypto->decrypt() returning: ' . $data;
				}
				return $data;
			}
		}
	} // class crypto
?>