Index: constraints/constraint_and.php
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/password_policy/constraints/constraint_and.php,v
retrieving revision 1.1
diff -u -r1.1 constraint_and.php
--- constraints/constraint_and.php	25 Sep 2006 05:01:19 -0000	1.1
+++ constraints/constraint_and.php	6 Nov 2007 16:01:00 -0000
@@ -1,58 +1,76 @@
-<?php
-include_once "constraint.php";
-
-class And_Constraint extends Constraint {
-	
-	var $constraints;
-	
-	function And_Constraint() {
-		$this->constraints = array();
-	}
-
-	function validate($plaintext_password, $user = NULL) {
-		foreach ($this->constraints as $constraint) {
-			if (!$constraint->validate($plaintext_password, $user)) {
-				return 0;	
-			}
-		}
-		return 1;
-	}
-	
-	function getDescription() {
-		return $this->getValidationErrorMessage();
-	}
-	
-	function getValidationErrorMessage($plaintext_password = NULL, $user = NULL) {
-		$reason = "<ul>";
-		
-		foreach ($this->constraints as $constraint) {
-			$msg = NULL;
-			
-			// if we have a password to test, then indicate in the validation error
-			// message, which constraints passed and which failed.
-			if (isset($plaintext_password)) {
-				if ($constraint->validate($plaintext_password, $user)) {
-					$msg = t("(PASS)") . " " . $constraint->getValidationErrorMessage();
-				}
-				else {
-					$msg = t("(FAIL)") . " " . $constraint->getValidationErrorMessage();
-				}
-			}
-			// else just display the constraint regardless of passing or failing
-			else {
-				$msg = $constraint->getValidationErrorMessage();
-			}
-			$reason .= "<li>$msg</li>";
-		}
-		return $reason . "</ul>";
-	}
-	
-	function addConstraint($constraint) {
-		array_push($this->constraints, $constraint);
-	}
-
-	function getConstraints() {
-		return $this->constraints;
-	}
-}
-?>
\ No newline at end of file
+<?php
+include_once "constraint.php";
+
+class And_Constraint extends Constraint {
+	
+	var $constraints;
+	var $expiration;
+	var $warning;
+	
+	function And_Constraint() {
+		$this->constraints = array();
+	}
+
+	function validate($plaintext_password, $user = NULL) {
+		foreach ($this->constraints as $constraint) {
+			if (!$constraint->validate($plaintext_password, $user)) {
+				return 0;	
+			}
+		}
+		return 1;
+	}
+	
+	function getDescription() {
+		return $this->getValidationErrorMessage();
+	}
+	
+	function getValidationErrorMessage($plaintext_password = NULL, $user = NULL) {
+		$reason = "<ul>";
+		
+		foreach ($this->constraints as $constraint) {
+			$msg = NULL;
+			
+			// if we have a password to test, then indicate in the validation error
+			// message, which constraints passed and which failed.
+			if (isset($plaintext_password)) {
+				if ($constraint->validate($plaintext_password, $user)) {
+					$msg = t("(PASS)") . " " . $constraint->getValidationErrorMessage();
+				}
+				else {
+					$msg = t("(FAIL)") . " " . $constraint->getValidationErrorMessage();
+				}
+			}
+			// else just display the constraint regardless of passing or failing
+			else {
+				$msg = $constraint->getValidationErrorMessage();
+			}
+			$reason .= "<li>$msg</li>";
+		}
+		return $reason . "</ul>";
+	}
+	
+	function addConstraint($constraint) {
+		array_push($this->constraints, $constraint);
+	}
+
+	function getConstraints() {
+		return $this->constraints;
+	}
+
+	function getExpiration() {
+		return $this->expiration;
+	}
+	
+	function setExpiration($expiration) {
+		$this->expiration = $expiration;
+	}
+
+	function getWarning() {
+		return $this->warning;
+	}
+	
+	function setWarning($warning) {
+		$this->warning = $warning;
+	}
+}
+?>
Index: password_policy.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/password_policy/password_policy.module,v
retrieving revision 1.3
diff -u -r1.3 password_policy.module
--- password_policy.module	3 Jan 2007 18:52:48 -0000	1.3
+++ password_policy.module	6 Nov 2007 16:40:12 -0000
@@ -1,5 +1,5 @@
 <?php
-/* $Id: password_policy.module,v 1.3 2007/01/03 18:52:48 dayre Exp $ */
+/* $Id: password_policy.module,v 1.2.2.1 2007/01/03 19:06:47 dayre Exp $ */
 
 /**
  * Help text for the password policy module.
@@ -10,7 +10,7 @@
 		case "admin/modules#description":
 			$output = t("Enforces a password policy on user accounts.");
 			break;
-		case "admin/password_policy":
+		case "admin/settings/password_policy":
 			$output = '<p>' . t('The password policy module allows you to enforce a specific level of password complexity ') .
 			t('for the user passwords on the system.') . '</p>';
 
@@ -23,11 +23,11 @@
 				t("Set default policy") . '</i>' . t(" button.");
 			}
 			else {
-				$output .= '<p>' . t('No policies are currently defined.  To add a new policy, click <a href="%url">add policy</a>.', array("%url" => url('admin/password_policy/add')));
+				$output .= '<p>' . t('No policies are currently defined.  To add a new policy, click <a href="@url">add policy</a>.', array("@url" => url('admin/settings/password_policy/add')));
 			}
 			break;
-		case "admin/password_policy/add":
-		case "admin/password_policy/edit/" . arg(3) :
+		case "admin/settings/password_policy/add":
+		case "admin/settings/password_policy/edit/" . arg(3) :
 			$output = '<p>';
 			if (arg(2) == 'add')
 				$output .= t("Give a name and descriptive comment to your new password policy.  ");
@@ -39,6 +39,8 @@
 			t("module can not determine these situations automatically, so be careful during the definition of your policy.") . '</p>';
 
 			break;
+		case "admin/password_policy/list_expired":
+			$output = '<p>'.t('List of accounts which passwords have expired.').'</p>';
 	}
 	return $output;
 }
@@ -58,56 +60,69 @@
 	$items = array();
 	$edit = user_access('administer password policies');
 	
-	$items[] = array('path' => 'admin/password_policy',
-      'title' => t('password policy'),
+	$items[] = array('path' => 'admin/settings/password_policy',
+      'title' => t('Password Policy'),
       'description' => t('Configures policies for user account passwords.'),
       'callback' => 'password_policy_view',
       'access' => $edit);
 
-	$items[] = array('path' => 'admin/password_policy/add',
-      'title' => t('add policy'),
+	$items[] = array('path' => 'admin/settings/password_policy/add',
+      'title' => t('Add Policy'),
       'callback' => 'password_policy_form_policy',
       'access' => $edit,
       'type' => MENU_LOCAL_TASK);
 
-	$arg3 = arg(3);
-	if (!empty($arg3) && is_numeric($arg3)) {
-		$items[] = array('path' => 'admin/password_policy/edit/'. arg(3),
+	$arg4 = arg(4);
+	if (!empty($arg4) && is_numeric($arg4)) {
+		$items[] = array('path' => 'admin/settings/password_policy/edit/'. arg(4),
 	          'title' => t("Edit password policy"),
 	          'callback' => 'password_policy_form_policy',
-	          'callback arguments' => array('id' => arg(3)),
+	          'callback arguments' => array('id' => arg(4)),
 	          'type' => MENU_CALLBACK,
 	          'access' => $edit
 		);
 		
-		$items[] = array('path' => 'admin/password_policy/delete/'. arg(3),
+		$items[] = array('path' => 'admin/settings/password_policy/delete/'. arg(4),
 	         'title' => t("Delete password policy"),
 	         'callback' => 'password_policy_delete',
-	         'callback arguments' => array('id' => arg(3)),
+	         'callback arguments' => array('id' => arg(4)),
 	         'type' => MENU_CALLBACK,
 	         'access' => $edit
 		);
 	}
 	
 	// we display the name of the policy when viewing
-	$arg2 = arg(2);
-	if (!empty($arg2) && is_numeric($arg2)) {
-		$policy = password_policy_load_policy_by_id($arg2);
-
-		$items[] = array('path' => 'admin/password_policy/'. $arg2,
-          'title' => $policy->name,
-          'callback' => 'password_policy_view',
-          'callback arguments' => array('id' => $arg2),
-          'type' => MENU_CALLBACK,
-          'access' => $edit
+	$arg3 = arg(3);
+	if (!empty($arg3) && is_numeric($arg3)) {
+		$policy = password_policy_load_policy_by_id($arg3);
+
+		$items[] = array('path' => 'admin/settings/password_policy/'. $arg3,
+      'title' => $policy->name,
+      'callback' => 'password_policy_view',
+      'callback arguments' => array('id' => $arg3),
+      'type' => MENU_CALLBACK,
+      'access' => $edit
 		);
 	}
 
-	$items[] = array('path' => 'admin/password_policy/list',
+	$items[] = array('path' => 'admin/settings/password_policy/list',
       'title' => t('list'),
       'type' => MENU_DEFAULT_LOCAL_TASK,
       'weight' => -10);
 
+	$items[] = array('path' => 'admin/settings/password_policy/list_expired',
+      'title' => t('expired accounts'),
+      'callback' => 'password_policy_list_expired',
+      'access' => $edit,
+      'type' => MENU_LOCAL_TASK);
+
+	$items[] = array('path' => 'admin/settings/password_policy/unblock/'. $arg3,
+      'title' => t('Unblock'),
+      'callback' => 'password_policy_unblock',
+      'callback arguments' => array('id' => $arg3),
+      'type' => MENU_CALLBACK,
+      'access' => $edit
+	);
 	return $items;
 }
 
@@ -118,8 +133,8 @@
  * default view, the user can set a new default password policy or clear the default so
  * that no policy is active and the default drupal password mechanism takes affect.
  */
-function password_policy_view_submit($form_id, $form_values) {
-	$op = !empty($_POST['op']) ? $_POST['op'] : '';
+function password_policy_view_form_submit($form_id, $form_values) {
+	$op = !empty($form_values['op']) ? $form_values['op'] : '';
 	if ($op == t('Clear default')) {
 		_password_policy_clear_default();
 		drupal_set_message(t('No policy is active, all user passwords will be accepted (Drupal default).'));
@@ -131,7 +146,8 @@
 			$policy = password_policy_load_policy_by_id($pid);
 			if ($policy) {
 				_password_policy_clear_default();
-				db_query("UPDATE {password_policy} SET enabled = %d WHERE id = %d", 1, $pid);
+				$time = time();
+				db_query("UPDATE {password_policy} SET enabled = %d, created = %d WHERE id = %d", 1, $time, $pid);
 				drupal_set_message(t('\'%name\' has been set as the default password policy.', array('%name' => $policy->name)));
 			}
 		}
@@ -156,43 +172,61 @@
 	if ($pid) {
 		$policy = password_policy_load_policy_by_id($pid);
 		if (!$policy) {
-			drupal_goto("admin/password_policy");
+			drupal_goto("admin/settings/password_policy");
 		}
-		$editURL = l(t('editing this policy'), 'admin/password_policy/edit/'. $pid);
+		$editURL = l(t('editing this policy'), 'admin/settings/password_policy/edit/'. $pid);
 		$constraints = $policy->constraints;
 		$desc = !$constraints ? t('This policy has no constraints set.  You can add constraints by ') . $editURL . '.'
 		: t('This policy has the constraints listed below.  You can change the constraints by ') . $editURL . '.</br>' .  $policy->getValidationErrorMessage();
 		$output = "<p>$desc</p>";
+
+                $expiration = $policy->expiration;
+		$desc = $expiration > 0 ? t('The passwords expire after %number %days.', array('%number' => $expiration, '%days' => format_plural($expiration, t('day'), t('days')))) : t('The passwords never expire.');
+		$output .= "<p>$desc</p>";
 		return $output;
 
 	}
 	
 	// load the summary policies (id->name)
 	$summaries = _password_policy_load_policy_summaries();
-
+  
 	if ($summaries) {
-		foreach ($summaries as $summary) {
-			$id = $summary['id'];
-			$name = $summary['name'];
-			$row = array();
-			$options[$id] = '';
-			if ($summary['enabled']) {
-				$default_id = $id;
-			}
-			$form[$name]['id'] = array('#value' => $id);
-			$form[$name]['view'] = array('#value' => l(t('view'), 'admin/password_policy/'. $id));
-			$form[$name]['edit'] = array('#value' => l(t('edit'), 'admin/password_policy/edit/'. $id));
-			$form[$name]['delete'] = array('#value' => l(t('delete'), 'admin/password_policy/delete/'. $id));
-		}
-		$form['default'] = array('#type' => 'radios', '#options' => $options, '#default_value' => $default_id);
-		$form['submit'] = array('#type' => 'submit', '#value' => t('Set default policy'));
-		$form['clear'] = array('#type' => 'submit', '#value' => t('Clear default'));
-		return drupal_get_form('password_policy_view', $form);
+		return drupal_get_form('password_policy_view_form', $summaries);
 	}
 	return "";
 }
 
-
+function password_policy_view_form($summaries) {
+  $form = array();
+  //$summaries = _password_policy_load_policy_summaries();
+  if ($summaries) {
+    foreach ($summaries as $summary) {
+	    //drupal_set_message('<pre>'.print_r($summary, 1).'</pre>');
+      $id = $summary['id'];
+      $name = $summary['name'];
+      $row = array();
+      $options[$id] = '';
+      if ($summary['enabled']) {
+        $default_id = $id;
+        $form[$id]['created'] = array('#value' => format_date($summary['created'], 'custom', 'm/d/y H:i:s'));
+      }
+      $form[$id]['id'] = array('#value' => $id);
+      $form[$id]['name'] = array('#value' => $name);
+      $form[$id]['view'] = array('#value' => l(t('view'), 'admin/settings/password_policy/'. $id));
+      $form[$id]['edit'] = array('#value' => l(t('edit'), 'admin/settings/password_policy/edit/'. $id));
+      $form[$id]['delete'] = array('#value' => l(t('delete'), 'admin/settings/password_policy/delete/'. $id));
+      //$options[$id] = $name;
+      //. l(t('view'), 'admin/password_policy/'. $id) . l(t('edit'), 'admin/password_policy/edit/'. $id) . l(t('delete'), 'admin/password_policy/delete/'. $id);
+    }
+    $form['default'] = array('#type' => 'radios', '#options' => $options, '#default_value' => $default_id);
+    $form['submit'] = array('#type' => 'submit', '#value' => t('Set default policy'));
+    $form['clear'] = array('#type' => 'submit', '#value' => t('Clear default'));
+	
+	  //drupal_set_message('<pre>'.print_r($form, 1).'</pre>');
+    
+    return $form;
+  }
+}
 
 /**
  * Custom theme for rendering a checkbox list of defined policies.  With Drupal's form
@@ -200,22 +234,26 @@
  * the default.  This layout is based on a similar layout found in the "input formats"
  * module.
  */
-function theme_password_policy_view($form) {
-	foreach ($form as $name => $element) {
+function theme_password_policy_view_form($form) {
+	foreach ($form as $id => $element) {
 		if (!empty($element['edit']) && is_array($element['edit'])) {
 			$rows[] = array(
-			form_render($form['default'][$element['id']['#value']]),
-			check_plain($name),
-			form_render($form[$name]['view']),
-			form_render($form[$name]['edit']),
-			form_render($form[$name]['delete'])
+  			drupal_render($form['default'][$element['id']['#value']]),
+  			check_plain($form[$id]['name']['#value']),
+  			$element['created']['#value'],
+  			drupal_render($form[$id]['view']),
+  			drupal_render($form[$id]['edit']),
+  			drupal_render($form[$id]['delete'])
 			);
-			unset($form[$name]);
+			unset($form[$id]);
 		}
 	}
-	$header = array(t('Default'), t('Name'), array('data' => t('Operations'), 'colspan' => 3));
+	$header = array(t('Default'), t('Name'), t('Enabled'), array('data' => t('Operations'), 'colspan' => 3));
 	$output = theme('table', $header, $rows);
-	$output .= form_render($form);
+	
+	//drupal_set_message('<pre>'.print_r($rows, 1).'</pre>');
+	
+	$output .= drupal_render($form);
 
 	return $output;
 }
@@ -225,7 +263,7 @@
  * in password_policy_delete_submit().
  */
 function password_policy_delete() {
-	$pid = arg(3);
+	$pid = arg(4);
 	if (!$pid) {
 		drupal_not_found();
 	}
@@ -233,31 +271,35 @@
 	$policy = password_policy_load_policy_by_id($pid);
 
 	if ($policy) {
-		$form['pid'] = array('#type' => 'hidden', '#value' => $pid);
-		$form['name'] = array('#type' => 'hidden', '#value' => $policy->name);
-
-		$description = count($policy->constraints)
-		? t('This policy has the following constraints:') . '<br/>' . $policy->getValidationErrorMessage()
-		: t('There are no constraints specified for this policy.');
-
-		return confirm_form(
-      	'password_policy_delete', 
-		$form,
-		t('Are you sure you want to delete the policy \'%name\'?', array('%name' => $policy->name)),
-        'admin/password_policy', 
-		$description,
-		t('Delete'),
-		t('Cancel'));
+		return drupal_get_form('password_policy_delete_confirm', $pid, $policy);
 	}
 	else {
 		drupal_not_found();
 	}
 }
 
+function password_policy_delete_confirm($pid, $policy) {
+  $form = array();
+  $form['pid'] = array('#type' => 'hidden', '#value' => $pid);
+  $form['name'] = array('#type' => 'hidden', '#value' => $policy->name);
+
+  $description = count($policy->constraints)
+    ? t('This policy has the following constraints:') . '<br/>' . $policy->getValidationErrorMessage()
+    : t('There are no constraints specified for this policy.');
+
+  return confirm_form(
+    $form,
+    t('Are you sure you want to delete the policy \'%name\'?', array('%name' => $policy->name)),
+        'admin/settings/password_policy', 
+    $description,
+    t('Delete')
+  );
+}
+
 /**
  * Submit hook for the delete policy operation.
  */
-function password_policy_delete_submit($form_id, $form_values) {
+function password_policy_delete_confirm_submit($form_id, $form_values) {
 	$pid = $form_values['pid'];
 	$policy = password_policy_load_policy_by_id($pid);
 
@@ -268,7 +310,7 @@
 		watchdog('password_policy', t('Policy \'%name\' was deleted.', array('%name' => $policy->name)), WATCHDOG_NOTICE);
 	}
 	
-	return 'admin/password_policy';
+	return 'admin/settings/password_policy';
 }
 
 /**
@@ -314,22 +356,57 @@
  */
 function password_policy_form_policy($pid = NULL) {
 	
-	if ($pid) {
+	return drupal_get_form('password_policy_form_policy_form', $pid);
+}
+
+function password_policy_form_policy_form($pid) {
+  $form = array();
+
+  if ($pid) {
 		$policy = password_policy_load_policy_by_id($pid);
 	}
 
-	$form['name'] = array('#type' => 'textfield',
+        $form['general'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('General Settings'),
+    '#collapsible' => FALSE,
+    '#collapsed' => FALSE
+        );
+
+	$form['general']['name'] = array('#type' => 'textfield',
     '#title' => t('Name'),
     '#default_value' => $policy->name,
     '#maxlength' => 64,
     '#required' => TRUE,
 	);
 
-	$form['description'] = array('#type' => 'textarea',
+	$form['general']['description'] = array('#type' => 'textarea',
     '#title' => t('Description'),
     '#default_value' => $policy->description,
 	);
 
+	$form['general']['expiration'] = array('#type' => 'textfield',
+    '#title' => t('Password Expiration'),
+    '#default_value' => $policy->expiration,
+    '#size' => 5,
+    '#maxlength' => 5,
+    '#description' => t('The passwords will expire after this number of days. The users with expired passwords will be blocked. Leaving this field empty won\'t put any password expiration constraints.'),
+	);
+
+	$form['general']['warning'] = array('#type' => 'textfield',
+    '#title' => t('Password Expiration Warning'),
+    '#default_value' => $policy->warning,
+    '#size' => 10,
+    '#description' => t('The comma separated list of days. The warning about expiration of the password will be sent out on those days before the expiration. Leaving this field empty won\'t send out or display any warnings.'),
+	);
+
+        $form['constraints'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Password Constraints'),
+    '#collapsible' => FALSE,
+    '#collapsed' => FALSE
+        );
+
 	// Get an array of the valid constraint types available
 	$constraintTypes = _password_policy_get_valid_constraints();
 
@@ -363,16 +440,15 @@
 	if ($policy) {
 		$form['pid'] = array('#type' => 'hidden', '#value' => $pid);
 	}
-	
-	return drupal_get_form('password_policy_form_policy', $form);
+
+  return $form;
 }
 
 
 /**
  * Form submission hook for new or edited password policies.
  */
-function password_policy_form_policy_submit($form_id, $form_values) {
-
+function password_policy_form_policy_form_submit($form_id, $form_values) {
 	// create the policy
 	_password_policy_load_constraint_definitions();
 
@@ -382,6 +458,8 @@
 	$policy = new And_Constraint();
 	$policy->setName($form_values['name']);
 	$policy->setDescription($form_values['description']);
+	$policy->setExpiration(trim($form_values['expiration']));
+	$policy->setWarning(str_replace(' ', '', $form_values['warning']));
 	foreach ($form_values as $key => $value) {
 		
 		// if we have no form value, then we have no constraint to set.
@@ -400,14 +478,14 @@
 	if ($form_values['pid']) {
 		password_policy_update_policy($form_values['pid'], $policy);
 		drupal_set_message(t('Policy \'%name\' has been updated.', array('%name' => $policy->name)));
-		watchdog('password_policy', t('Policy \'%name\' updated.', array('%name' => $policy->name)), WATCHDOG_NOTICE, l(t('view'), 'admin/password_policy/' . $form_values['pid']));
+		watchdog('password_policy', t('Policy \'%name\' updated.', array('%name' => $policy->name)), WATCHDOG_NOTICE, l(t('view'), 'admin/settings/password_policy/' . $form_values['pid']));
 	}
 	else {
 		password_policy_save_policy($policy);
-		watchdog('password_policy', t('New policy \'%name\' added.', array('%name' => $policy->name)), WATCHDOG_NOTICE, l(t('view'), 'admin/password_policy'));
+		watchdog('password_policy', t('New policy \'%name\' added.', array('%name' => $policy->name)), WATCHDOG_NOTICE, l(t('view'), 'admin/settings/password_policy'));
 	}
 
-	return "admin/password_policy";
+	return "admin/settings/password_policy";
 }
 
 /**
@@ -428,6 +506,9 @@
 				// as long as the password policy module is enabled, we will track the hashed password values which 
 				// can then be used in the history constraint.
 				if ($user->uid) _password_policy_store_password($user->uid, $edit['pass']);
+				// if user successfully changed his password we will unblock the account
+                                db_query("UPDATE {users} SET status = '1' WHERE uid = %d", $user->uid);
+                                db_query("DELETE FROM {password_policy_expiration} WHERE uid = '%d'", $user->uid);
 			
 			}
 		}
@@ -438,6 +519,62 @@
 			if ($user->uid) _password_policy_store_password($user->uid, $edit['pass']);
 		}
 	}
+
+	if ($type == 'login') {
+	    $constraint = password_policy_load_active_policy();
+	    // $edit['name'] is NULL for a one time login
+	    if($constraint && ($user->uid > 1 || variable_get('password_policy_admin', false)) && !empty($edit['name'])) {
+	        $expiration = $constraint->getExpiration();
+	        $warning = max(explode(',', $constraint->getWarning()));
+	        
+		$expiration_seconds = $expiration*60*60*24;
+	        $warning_seconds = $warning*60*60*24;
+
+                $policy_enabled = _password_policy_enebled($expiration_seconds);
+            }
+	    if(!empty($expiration)) {
+                $result = db_query("SELECT * FROM {password_policy_users} WHERE uid = %d ORDER BY created DESC LIMIT 1", $user->uid);
+                if ($row = db_fetch_object($result)) {
+                    $last_change = $row->created;
+                }
+                else {
+		    // user has not changed his pwd after this module had been enabled
+	            $last_change = $user->created;
+	        }
+
+                $time = time();
+		if($time > max($policy_enabled, $last_change) + $expiration_seconds) {
+                    db_query("UPDATE {users} SET status = '0' WHERE uid = '%d'", $user->uid);
+                    $result = db_query("SELECT * FROM {password_policy_expiration} WHERE uid = '%d'", $user->uid);
+                    if($row = db_fetch_array($result)) {
+                      db_query("UPDATE {password_policy_expiration} SET blocked = '%d' WHERE uid = %d", $time, $user->uid);
+		    }
+		    else {
+                      db_query("INSERT INTO {password_policy_expiration} (uid, blocked) VALUES ('%d', '%d')", $user->uid, $time);
+		    }
+		    watchdog('password_policy', t('Password for user %name has expired.', array('%name' => theme('placeholder', $user->name))), WATCHDOG_NOTICE, l(t('edit'), 'user/'.$user->uid.'/edit'));
+	            if(variable_get('password_policy_block', 0) == 0) {
+		        user_logout();
+		    }
+		    else {
+		        drupal_set_message(t('Your password has expired. You have to change it now or you won\'t be able to login again.'), 'error');
+		        unset($_REQUEST['destination']);
+                        drupal_goto('user/'.$user->uid.'/edit');
+                    }
+		}
+		elseif ($time > max($policy_enabled, $last_change) + $expiration_seconds - $warning_seconds) {
+		    $days_left = ceil((max($policy_enabled, $last_change) + $expiration_seconds - $time)/(60*60*24));
+	            drupal_set_message(t('Your password will expire in less than %number %days. Please change it.', array('%number' => $days_left, '%days' => format_plural($days_left, t('day'), t('days')))));
+		    unset($_REQUEST['destination']);
+		    drupal_goto('user/'.$user->uid.'/edit');
+		}
+            }
+	}
+
+        if($type == 'delete') {
+          db_query("DELETE FROM {password_policy_users} WHERE uid = '%d'", $user->uid);
+          db_query("DELETE FROM {password_policy_expiration} WHERE uid = '%d'", $user->uid);
+	}
 }
 
 /**
@@ -495,7 +632,7 @@
  * 		An array of associative arrays.
  */
 function _password_policy_load_policy_summaries() {
-	$result = db_query('SELECT id, name, enabled, description FROM {password_policy} p ORDER BY name');
+	$result = db_query('SELECT id, name, enabled, description, created FROM {password_policy} ORDER BY name');
 	while ($ary = db_fetch_array($result)) {
 		$summaries[] = $ary;
 	}
@@ -566,4 +703,244 @@
 	return array_keys($tests);
 }
 
-?>
+/**
+ * Implementation of hook_settings 
+ */
+function password_policy_settings() {
+  $form['expiration'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Expiration Settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => FALSE
+  );
+  $form['expiration']['password_policy_admin'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Admin (UID=1) password expires.'),
+    '#default_value' => variable_get('password_policy_admin', false),
+    '#description' => t('Admin account password will obey expiration policy.'),
+  );
+  $form['expiration']['password_policy_begin'] = array(
+    '#type' => 'radios',
+    '#title' => t('Beginning of password expirations'),
+    '#default_value' => variable_get('password_policy_begin', 0),
+    '#options' => array('0' => t('After expiration time from setting a default policy (all passwords are valid during the expiration time from setting the default policy, and after that older than expiration time passwords expire).'), '1' => t('Setting a default policy (passwords older than expiration time expire after setting the default policy, retroactive behaviour).')),
+  );
+  $form['expiration']['password_policy_block'] = array(
+    '#type' => 'radios',
+    '#title' => t('Blocking expired accounts'),
+    '#default_value' => variable_get('password_policy_block', 0),
+    '#options' => array('0' => t('Expired accounts are blocked. Only administrators can unblock them.'), '1' => t('The user with expired account is not blocked, but sent to a change password page. If the password is not changed, the account is blocked and the user cannot login again.')),
+  );
+
+  // E-mail notification settings.
+  $form['email'] = array(
+    '#type' => 'fieldset', 
+    '#title' => t('E-mail notification settings'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE
+  );
+  $form['email']['password_policy_mail_warning_subject'] = array(
+    '#type' => 'textfield', 
+    '#title' => t('Subject of warning e-mail'), 
+    '#default_value' => _password_policy_mail_text('warning_subject'), 
+    '#maxlength' => 180, 
+    '#description' => t('Customize the subject of the warning e-mail message, which is sent to remind of password expiration.') .' '. t('Available variables are:') .' %username, %site, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri, %days.',
+  );
+  $form['email']['password_policy_mail_warning_body'] = array(
+    '#type' => 'textarea', 
+    '#title' => t('Body of warning e-mail'), 
+    '#default_value' => _password_policy_mail_text('warning_body'), 
+    '#rows' => 15, 
+    '#description' => t('Customize the body of the warning e-mail message, which is sent to remind of password expiration.') .' '. t('Available variables are:') .' %username, %site, %uri, %uri_brief, %mailto, %date, %login_uri, %edit_uri, %days.',
+  );
+
+  return $form;
+}
+
+/**
+ * List all expired accounts 
+ */
+function password_policy_list_expired() {
+  $header[] = array('data' => t('Blocked'), 'field' => 'blocked', 'sort' => 'desc');
+  $header[] = array('data' => t('Username'), 'field' => 'name');
+  $header[] = array('data' => t('Unblocked'), 'field' => 'unblocked');
+  $header[] = array('data' => t('Action'));
+
+  $max_pages = 20;
+  $result = pager_query("SELECT p.*, u.name FROM {password_policy_expiration} p INNER JOIN {users} u ON p.uid = u.uid WHERE p.blocked > 0".tablesort_sql($header), $max_pages, 0, NULL);
+  while($row = db_fetch_object($result)) {
+    $entry[$row->uid]['blocked'] = format_date($row->blocked, 'custom', 'm/d/y H:i:s');
+    $entry[$row->uid]['name'] = l($row->name, 'user/'.$row->uid);
+    $entry[$row->uid]['unblocked'] = $row->unblocked < $row->blocked ? '' : format_date($row->unblocked, 'custom', 'm/d/y H:i:s');
+    $entry[$row->uid]['action'] = $row->unblocked < $row->blocked ? l(t('unblock'), 'admin/password_policy/unblock/'.$row->uid) : '';
+  }
+  if(!isset($entry)) {
+    $colspan = '4';
+    $entry[] = array(array('data' => t('No entries'), 'colspan' => $colspan));
+  }
+
+  $page = theme_table($header, $entry);
+  $page .= theme_pager(array(), $max_pages, 0);
+
+
+  return $page;
+}
+
+/**
+ * Unblocks the expired account
+ */
+function password_policy_unblock($uid = NULL) {
+  if($uid) {
+    db_query("UPDATE {users} SET status = '1' WHERE uid = %d", $uid);
+    db_query("UPDATE {password_policy_expiration} SET unblocked = '%d' WHERE uid = %d", time(), $uid);
+
+    if($account = user_load(array('uid' => $uid, 'status' => 1))) {
+      password_policy_send_login($account);
+      drupal_set_message(t('The user %name has been unblocked.', array('%name' => theme('placeholder', $account->name))));
+    }
+  }
+
+  drupal_goto('admin/password_policy/list_expired');
+}
+
+/**
+ * Sends one time login url to the user
+ * based on 'user_pass_submit'
+ */
+function password_policy_send_login($account = NUL) {
+  global $base_url;
+
+  $from = variable_get('site_mail', ini_get('sendmail_from'));
+
+  // Mail one time login URL and instructions.
+  $variables = array('%username' => $account->name, '%site' => variable_get('site_name', 'drupal'), '%login_url' => user_pass_reset_url($account), '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $account->mail, '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE));
+  $subject = _user_mail_text('pass_subject', $variables);
+  $body = _user_mail_text('pass_body', $variables);
+  $headers = "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from";
+  $mail_success = user_mail($account->mail, $subject, $body, $headers);
+
+  if ($mail_success) {
+    watchdog('password_policy', t('Password reset instructions mailed to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))));
+    drupal_set_message(t('Further instructions have been sent to %name e-mail address.', array('%name' => theme('placeholder', $account->name))));
+  }
+  else {
+    watchdog('password_policy', t('Error mailing password reset instructions to %name at %email.', array('%name' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))), WATCHDOG_ERROR);
+    drupal_set_message(t('Unable to send mail. Please contact the site admin.'));
+  }
+}
+
+/**
+ * Implementation of hook_cron
+ *
+ */
+function password_policy_cron() {
+  $constraint = password_policy_load_active_policy();
+  if($constraint) {
+    $expiration = $constraint->getExpiration();
+    $warnings = explode(',', $constraint->getWarning());
+
+    if(!empty($expiration)) {
+      // Get all users' last password change time. We don't touch blocked accounts
+      $result = db_query("SELECT u.*, u.created created_u, p.created created_p, e.warning warning, e.unblocked unblocked FROM {users} u LEFT JOIN {password_policy_users} p ON u.uid = p.uid LEFT JOIN {password_policy_expiration} e ON u.uid = e.uid WHERE u.uid > 0 AND u.status = '1' ORDER BY p.created ASC");
+      while($row = db_fetch_object($result)) {
+        if($row->uid == 1 && !variable_get('password_policy_admin', false)) { continue; }
+        $accounts[$row->uid] = empty($row->created_p) ? $row->created_u : $row->created_p;
+        $warns[$row->uid] = $row->warning;
+        $unblocks[$row->uid] = $row->unblocked;
+      }
+
+      $expiration_seconds = $expiration*60*60*24;
+      $policy_enabled = _password_policy_enebled($expiration_seconds);
+      rsort($warnings, SORT_NUMERIC);
+      $time = time();
+      foreach($accounts as $uid => $last_change) {
+        foreach($warnings as $warning) {
+          if(!empty($warning)) {
+            $warning_seconds = $warning*60*60*24;
+            $start_period = max($policy_enabled, $last_change) + $expiration_seconds - $warning_seconds;
+            $end_period = $start_period + 60*60*24;
+            if($warns[$uid] > $start_period && $warns[$uid] < $end_period) {
+              // a warning was already mailed out
+              continue;
+            }
+            if($time > $start_period && $time < $end_period) {
+              // we're sending a warning
+              global $base_url;
+
+              $from = variable_get('site_mail', ini_get('sendmail_from'));
+              $account = user_load(array('uid' => $uid));
+              $variables = array('%username' => $account->name, '%site' => variable_get('site_name', 'drupal'), '%uri' => $base_url, '%uri_brief' => substr($base_url, strlen('http://')), '%mailto' => $account->mail, '%date' => format_date(time()), '%login_uri' => url('user', NULL, NULL, TRUE), '%edit_uri' => url('user/'. $account->uid .'/edit', NULL, NULL, TRUE), '%days' => $warning);
+              $subject = _password_policy_mail_text('warning_subject', $variables);
+              $body = _password_policy_mail_text('warning_body', $variables);
+              $headers = "From: $from\nReply-to: $from\nX-Mailer: Drupal\nReturn-path: $from\nErrors-to: $from";
+              $mail_success = user_mail($account->mail, $subject, $body, $headers);
+
+              if ($mail_success) {
+                watchdog('password_policy', t('Password expiration warning mailed to %username at %email.', array('%username' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))));
+              }
+              else {
+                watchdog('password_policy', t('Error mailing password expiration warning to %username at %email.', array('%username' => theme('placeholder', $account->name), '%email' => theme('placeholder', $account->mail))));
+              }
+
+              if(!empty($warns[$uid])) {
+                db_query("UPDATE {password_policy_expiration} SET warning = '%d' WHERE uid = %d", $time, $uid);
+              }
+              else {
+                db_query("INSERT INTO {password_policy_expiration} (uid, warning) VALUES ('%d', '%d')", $uid, $time);
+              }
+	    }
+          }
+        }
+
+        // Block expired accounts. Unblocked accounts are not blocked for 24h.
+        if($time > max($policy_enabled, $last_change) + $expiration_seconds && $time > $unblocks[$uid] + 60*60*24 && variable_get('password_policy_block', 0) == 0) {
+          db_query("UPDATE {users} SET status = '0' WHERE uid = '%d'", $uid);
+          if(!empty($warns[$uid])) {
+            db_query("UPDATE {password_policy_expiration} SET blocked = '%d' WHERE uid = %d", $time, $uid);
+          }
+          else {
+            db_query("INSERT INTO {password_policy_expiration} (uid, blocked) VALUES ('%d', '%d')", $uid, $time);
+          }
+
+          $account = user_load(array('uid' => $uid));
+          watchdog('password_policy', t('Password for user %name has expired.', array('%name' => theme('placeholder', $account->name))), WATCHDOG_NOTICE, l(t('edit'), 'user/'.$account->uid.'/edit'));
+        }
+      }
+    }
+  }
+}
+
+/**
+ * Loads default or saved mail text
+ */
+function _password_policy_mail_text($messageid, $variables = array()) {
+  // Check if an admin setting overrides the default string.
+  if ($admin_setting = variable_get('password_policy_mail_' . $messageid, '')) {
+    return strtr($admin_setting, $variables);
+  }
+  // No override, return with default strings.
+  else {
+    switch ($messageid) {
+      case 'warning_subject':
+        return t('Password expiration warning for %username at %site', $variables);
+      case 'warning_body':
+        return t("%username,\n\nYour password at %site will expire in less than %days day(s).\n\nPlease go to %edit_uri to change your password.", $variables);
+    }
+  }
+}
+
+/**
+ * Returns starting point of active policy
+ */
+function _password_policy_enebled($expiration_seconds = 0) {
+  $result = db_query("SELECT * FROM {password_policy} WHERE enabled = '1' ORDER BY enabled DESC LIMIT 1");
+  if ($row = db_fetch_object($result)) {
+    $policy_enabled = $row->created;
+  }
+  if(variable_get('password_policy_begin', 0) == 1) {
+    // password older than expiration time expires starting from setting the policy
+    $policy_enabled -= $expiration_seconds;
+  }
+
+  return $policy_enabled;
+}
\ No newline at end of file
--- E:/Web Sites/drupal/head/contributions/modules/password_policy/password_policy.info
+++ E:/Web Sites/drupal/head/contributions/modules/password_policy/password_policy.info
@@ -0,0 +1,4 @@
+; $Id$
+name = "Password Policy"
+description = "Set policy to ensure good password security."
+version = "$Name$"


