<?php
/**
* @version	$Id: password_formatter.php 14758 2011-11-11 10:39:49Z alex $
* @package	In-Portal
* @copyright	Copyright (C) 1997 - 2011 Intechnic. All rights reserved.
* @license      GNU/GPL
* In-Portal is Open Source software.
* This means that this software may have been modified pursuant
* the GNU General Public License, and as distributed it includes
* or is derivative of works licensed under the GNU General Public License
* or other free or open source software licenses.
* See http://www.in-portal.org/license for copyright notices and details.
*/

defined('FULL_PATH') or die('restricted access!');

class kPasswordFormatter extends kFormatter
{
	/**
	 * The method is supposed to alter config options or cofigure object in some way based on its usage of formatters
	 * The methods is called for every field with formatter defined when configuring item.
	 * Could be used for adding additional VirtualFields to an object required by some special Formatter
	 *
	 * @param string $field_name
	 * @param array $field_options
	 * @param kDBBase $object
	 */
	function PrepareOptions($field_name, &$field_options, &$object)
	{
		if ( isset( $field_options['verify_field'] ) ) {
			$add_fields = Array ();
			$options = Array ('master_field' => $field_name, /*'error_field' => $field_name,*/ 'formatter' => 'kPasswordFormatter');

			$copy_options = Array ('encryption_method', 'salt', 'required', 'skip_empty');
			foreach ($copy_options as $copy_option) {
				if (array_key_exists($copy_option, $field_options)) {
					$options[$copy_option] = $field_options[$copy_option];
				}
			}

			$add_fields[ $field_options['verify_field'] ] = $options;

			$add_fields[$field_name.'_plain'] = Array('type'=>'string', 'error_field'=>$field_name);
			$add_fields[ $field_options['verify_field'].'_plain' ] = Array('type'=>'string', 'error_field'=>$field_options['verify_field'] );

			$virtual_fields = $object->getVirtualFields();
			$add_fields = kUtil::array_merge_recursive($add_fields, $virtual_fields);
			$object->setVirtualFields($add_fields);
		}
	}

	/**
	 * Formats value of a given field
	 *
	 * @param string $value
	 * @param string $field_name
	 * @param kDBItem|kDBList $object
	 * @param string $format
	 * @return string
	 */
	function Format($value, $field_name, &$object, $format=null)
	{
		return $value;
	}

	/**
	 * Performs password & verify password field validation
	 *
	 * @param mixed $value
	 * @param string $field_name
	 * @param kDBItem $object
	 * @return mixed
	 * @access public
	 */
	public function Parse($value, $field_name, &$object)
	{
		$options = $object->GetFieldOptions($field_name);

		$flip_count = 0;
		$fields_set = true;
		$password_field = $verify_field = '';
		$fields = Array ('master_field', 'verify_field');

		// 1. collect values from both Password and VerifyPassword fields
		while ($flip_count < 2) {
			if ( getArrayValue($options, $fields[0]) ) {
				$tmp_field = $options[ $fields[0] ];
				$object->SetDBField($field_name.'_plain', $value);

				if ( !$object->GetFieldOption($tmp_field, $fields[1].'_set') ) {
					$object->SetFieldOption($tmp_field, $fields[1].'_set', true);
				}

				$password_field = $options[ $fields[0] ];
				$verify_field = $field_name;
			}

			$fields = array_reverse($fields);
			$flip_count++;
		}

		$salt = $object->GetFieldOption($password_field, 'salt', false, '');

		if ($object->GetFieldOption($password_field, 'verify_field_set') && $object->GetFieldOption($verify_field, 'master_field_set')) {
			$new_password = $object->GetDBField($password_field . '_plain');
			$verify_password = $object->GetDBField($verify_field . '_plain');

			if ($new_password == '' && $verify_password == '') {
				// both passwords are empty -> keep old password
				if ($object->GetDBField($password_field) != $this->EncryptPassword('', $salt)) {
					if ($options['encryption_method'] == 'plain') {
						return $value;
					}

					return $this->EncryptPassword($value);
				}
				else {
					return $value;
				}
			}

			// determine admin or front
			$phrase_error_prefix = $this->Application->isAdmin ? 'la' : 'lu';

			if ($new_password != $verify_password) {
				// passwords don't match (no matter what is their length)
				$object->SetError($verify_field, 'passwords_do_not_match', $phrase_error_prefix.'_passwords_do_not_match');
			}

			$min_length = $this->Application->ConfigValue('Min_Password'); // for error message too
			$min_length = $object->GetFieldOption($password_field, 'min_length', false, $min_length);

			if (mb_strlen($new_password) < $min_length) {
				$error_msg = '+' . sprintf($this->Application->Phrase($phrase_error_prefix.'_passwords_too_short'), $min_length); // + -> not phrase
				$object->SetError($password_field, 'passwords_min_length', $error_msg);
			}
		}

		if ($value == '') {
			return $object->GetDBField($field_name);
		}

		if ($options['encryption_method'] == 'plain') {
			return $value;
		}

		return $this->EncryptPassword($value, $salt);
	}

	function EncryptPassword($value, $salt=null)
	{
		if (!isset($salt) || !$salt) {
			// if empty salt, assume, that it's not passed at all
			return md5($value);
		}
		return md5(md5($value).$salt);
	}
}