View source
<?php
function password_policy_help($section = '') {
$output = '';
switch ($section) {
case 'admin/user/password_policy':
$output = '<p>' . t('The password policy module allows you to enforce a specific level of password complexity for the user passwords on the system.') . '</p>';
if (password_policy_get_policy_count() != 0) {
$output .= '<p>' . t('Listed below are the currently defined password policies. If no policy is set as the default, then any password will be accepted by the system (the Drupal default).') . '</p><p>' . t('To set a new default password policy, select the policy below and click the ') . '<em>' . t('Set default policy') . '</em>' . 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/user/password_policy/add'),
));
}
break;
case 'admin/user/password_policy/add':
case 'admin/user/password_policy/edit/' . arg(3):
$output = '<p>';
if (arg(2) == 'add') {
$output .= t('Give a name and descriptive comment to your new password policy. ');
}
$output .= t("A specific level of required password complexity can be achieved by adding minimum requirements for the constraints listed below. ") . t("If no minimum requirements are specified for a constraint, then that constraint will be ignored. ") . t("Only the constraints given values will be used.") . '</p><p>' . t("Please note that it is very easy to specify a set of constraints which can NEVER be satisfied (eg. min length = 3, min uppercase = 3, min lowercase = 3).") . t("This module can not determine these situations automatically, so be careful during the definition of your policy.") . '</p>';
break;
case 'admin/user/password_policy/list_expired':
$output = '<p>' . t('List of accounts which passwords have expired.') . '</p>';
}
return $output;
}
function password_policy_perm() {
return array(
'administer password policies',
);
}
function password_policy_menu($may_cache) {
$items = array();
if (!$may_cache) {
$items[] = array(
'path' => 'admin/settings/password_policy',
'title' => t('Password policy'),
'description' => t('Configures policies for user account passwords.'),
'callback' => 'drupal_get_form',
'callback arguments' => array(
'password_policy_admin_settings',
),
'access' => user_access('administer site configuration'),
);
}
$items[] = array(
'path' => 'admin/user/password_policy',
'title' => t('Password policy'),
'description' => t('Configures policies for user account passwords.'),
'callback' => 'password_policy_view',
'access' => user_access('administer password policies'),
);
$items[] = array(
'path' => 'admin/user/password_policy/add',
'title' => t('Add policy'),
'callback' => 'password_policy_form_policy',
'access' => user_access('administer password policies'),
'type' => MENU_LOCAL_TASK,
);
$arg4 = arg(4);
if (!empty($arg4) && is_numeric($arg4)) {
$items[] = array(
'path' => 'admin/user/password_policy/edit/' . arg(4),
'title' => t('Edit password policy'),
'callback' => 'password_policy_form_policy',
'callback arguments' => array(
'id' => arg(4),
),
'type' => MENU_CALLBACK,
'access' => user_access('administer password policies'),
);
$items[] = array(
'path' => 'admin/user/password_policy/delete/' . arg(4),
'title' => t('Delete password policy'),
'callback' => 'password_policy_delete',
'callback arguments' => array(
'id' => arg(4),
),
'type' => MENU_CALLBACK,
'access' => user_access('administer password policies'),
);
}
$arg3 = arg(3);
if (!empty($arg3) && is_numeric($arg3)) {
$policy = password_policy_load_policy_by_id(arg(3));
$items[] = array(
'path' => 'admin/user/password_policy/' . arg(3),
'title' => $policy->name,
'callback' => 'password_policy_view',
'callback arguments' => array(
'id' => arg(3),
),
'type' => MENU_CALLBACK,
'access' => user_access('administer password policies'),
);
}
$items[] = array(
'path' => 'admin/user/password_policy/list',
'title' => t('List'),
'type' => MENU_DEFAULT_LOCAL_TASK,
'weight' => -10,
);
$items[] = array(
'path' => 'admin/user/password_policy/list_expired',
'title' => t('Expired accounts'),
'callback' => 'password_policy_list_expired',
'access' => user_access('administer password policies'),
'type' => MENU_LOCAL_TASK,
);
$items[] = array(
'path' => 'admin/user/password_policy/unblock/' . arg(4),
'title' => t('Unblock'),
'callback' => 'password_policy_unblock',
'callback arguments' => array(
'id' => arg(4),
),
'type' => MENU_CALLBACK,
'access' => user_access('administer password policies'),
);
return $items;
}
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).'));
}
else {
if ($op == t('Set default policy')) {
$pid = $form_values['default'];
if ($pid) {
$policy = password_policy_load_policy_by_id($pid);
if ($policy) {
_password_policy_clear_default();
$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,
)));
}
}
}
}
}
function _password_policy_clear_default() {
db_query("UPDATE {password_policy} SET enabled = 0");
}
function password_policy_view($pid = NULL) {
if ($pid) {
$policy = password_policy_load_policy_by_id($pid);
if (!$policy) {
drupal_goto('admin/user/password_policy');
}
$edit_url = l(t('editing this policy'), 'admin/user/password_policy/edit/' . $pid);
$constraints = $policy->constraints;
$desc = !$constraints ? t('This policy has no constraints set. You can add constraints by ') . $edit_url . '.' : t('This policy has the constraints listed below. You can change the constraints by ') . $edit_url . '.<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;
}
$summaries = _password_policy_load_policy_summaries();
if ($summaries) {
return drupal_get_form('password_policy_view_form', $summaries);
}
return '';
}
function password_policy_view_form($summaries) {
$form = array();
if ($summaries) {
foreach ($summaries as $summary) {
$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/user/password_policy/' . $id),
);
$form[$id]['edit'] = array(
'#value' => l(t('edit'), 'admin/user/password_policy/edit/' . $id),
);
$form[$id]['delete'] = array(
'#value' => l(t('delete'), 'admin/user/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 $form;
}
}
function theme_password_policy_view_form($form) {
foreach ($form as $id => $element) {
if (!empty($element['edit']) && is_array($element['edit'])) {
$rows[] = array(
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[$id]);
}
}
$header = array(
t('Default'),
t('Name'),
t('Enabled'),
array(
'data' => t('Operations'),
'colspan' => 3,
),
);
$output = theme('table', $header, $rows);
$output .= drupal_render($form);
return $output;
}
function password_policy_delete() {
$pid = arg(4);
if (!$pid) {
drupal_not_found();
}
$policy = password_policy_load_policy_by_id($pid);
if ($policy) {
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/user/password_policy', $description, t('Delete'));
}
function password_policy_delete_confirm_submit($form_id, $form_values) {
$pid = $form_values['pid'];
$policy = password_policy_load_policy_by_id($pid);
db_query("DELETE FROM {password_policy} WHERE id = %d", $pid);
if (db_affected_rows()) {
drupal_set_message(t('Password policy \'%policy\' was deleted.', array(
'%policy' => $policy->name,
)));
watchdog('password_policy', t('Policy \'%name\' was deleted.', array(
'%name' => $policy->name,
)), WATCHDOG_NOTICE);
}
return 'admin/user/password_policy';
}
function _password_policy_get_valid_constraints() {
_password_policy_load_constraint_definitions();
return array(
new Length_Constraint(1),
new Letter_Constraint(1),
new Digit_Constraint(1),
new Letter_Digit_Constraint(1),
new Digit_Placement_Constraint(1),
new Lowercase_Constraint(1),
new Uppercase_Constraint(1),
new Punctuation_Constraint(1),
new History_Constraint(),
new Character_Types_Constraint(1),
new Delay_Constraint(1),
new Username_Constraint(),
);
}
function password_policy_form_policy($pid = NULL) {
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['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['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,
);
$count = 1;
foreach (_password_policy_get_valid_constraints() as $constraint) {
$default_value = NULL;
if ($policy) {
foreach ($policy->constraints as $policy_constraint) {
if (get_class($policy_constraint) == get_class($constraint)) {
$default_value = $policy_constraint
->getMinimumConstraintValue();
}
}
}
$form['constraints'][get_class($constraint)] = array(
'#type' => 'textfield',
'#size' => 5,
'#default_value' => $default_value,
'#maxlength' => 2,
'#title' => $constraint
->getName(),
'#description' => $constraint
->getDescription(),
);
$count++;
}
$form['submit'] = array(
'#type' => 'submit',
'#value' => $policy ? t('Edit policy') : t('Create policy'),
);
if ($policy) {
$form['pid'] = array(
'#type' => 'hidden',
'#value' => $pid,
);
}
return $form;
}
function password_policy_form_policy_form_submit($form_id, $form_values) {
_password_policy_load_constraint_definitions();
$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) {
$value = trim($value);
if ($value != '' && preg_match("/^.*constraint\$/i", $key)) {
$class_name = $key;
if (class_exists($class_name)) {
$constraint = new $class_name($value);
$constraint->minimumConstraintValue = $value;
$policy
->addConstraint($constraint);
}
}
}
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/user/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/user/password_policy'));
}
return "admin/user/password_policy";
}
function password_policy_user($type, &$edit, &$user, $category = NULL) {
if ($category == 'account' && !empty($edit['pass'])) {
if ($type == 'validate') {
$constraint = password_policy_load_active_policy();
if ($constraint && $constraint
->validate($edit['pass'], $user) == FALSE) {
form_set_error('pass', t('Your password must meet the following requirements:') . $constraint
->getValidationErrorMessage($edit['pass'], $user));
}
else {
if ($user->uid) {
_password_policy_store_password($user->uid, $edit['pass']);
}
db_query("UPDATE {users} SET status = 1 WHERE uid = %d", $user->uid);
db_query("DELETE FROM {password_policy_expiration} WHERE uid = %d", $user->uid);
}
}
else {
if ($type == 'insert' && !empty($edit['pass'])) {
if ($user->uid) {
_password_policy_store_password($user->uid, $edit['pass']);
}
}
}
}
if ($type == 'login') {
$constraint = password_policy_load_active_policy();
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_enabled($expiration_seconds);
}
if (!empty($expiration)) {
$result = db_query_range("SELECT * FROM {password_policy_users} WHERE uid = %d ORDER BY created DESC", $user->uid, 0, 1);
if ($row = db_fetch_object($result)) {
$last_change = $row->created;
}
else {
$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' => $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);
}
}
function _password_policy_store_password($uid, $pass) {
db_query("INSERT INTO {password_policy_users} SET uid = %d, pass = '%s', created = %d", $uid, md5($pass), time());
}
function password_policy_get_policy_count() {
$result = db_result(db_query("SELECT COUNT(id) FROM {password_policy}"));
return $result[0];
}
function password_policy_save_policy($policy) {
$cid = db_next_id('{password_policy}_id');
db_query("INSERT INTO {password_policy} SET id = %d, name = '%s', description = '%s', enabled = %d, serialized_policy = '%s'", $cid, $policy->name, $policy->description, 0, serialize($policy));
drupal_set_message(t('Policy \'%name\' has been updated.', array(
'%name' => $policy->name,
)));
}
function password_policy_update_policy($pid, $policy) {
db_query("UPDATE {password_policy} SET name = '%s', description = '%s', serialized_policy = '%s' WHERE id = %d", $policy->name, $policy->description, serialize($policy), $pid);
}
function _password_policy_load_policy_summaries() {
$result = db_query('SELECT id, name, enabled, description, created FROM {password_policy} ORDER BY name');
while ($ary = db_fetch_array($result)) {
$summaries[] = $ary;
}
return $summaries;
}
function password_policy_load_active_policy() {
_password_policy_load_constraint_definitions();
$result = db_query('SELECT * FROM {password_policy} p WHERE p.enabled = 1');
if (!$result || !db_num_rows($result)) {
return NULL;
}
$values = db_fetch_array($result);
return unserialize($values['serialized_policy']);
}
function password_policy_load_policy_by_id($id) {
_password_policy_load_constraint_definitions();
$result = db_query('SELECT * FROM {password_policy} p WHERE p.id = %d', $id);
if (!$result || !db_num_rows($result)) {
return NULL;
}
$values = db_fetch_array($result);
return unserialize($values['serialized_policy']);
}
function _password_policy_load_constraint_definitions() {
$dir = dirname(__FILE__) . '/constraints';
$constraints = file_scan_directory($dir, '^constraint.*\\.php$');
foreach ($constraints as $c_file) {
if (is_file($c_file->filename)) {
include_once $c_file->filename;
}
}
}
function password_policy_simpletest() {
$dir = drupal_get_path('module', 'password_policy');
$tests = file_scan_directory($dir, '\\.test$');
return array_keys($tests);
}
function password_policy_admin_settings() {
$form = array();
$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.'),
),
);
$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 system_settings_form($form);
}
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/user/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;
}
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' => $account->name,
)));
}
}
drupal_goto('admin/user/password_policy/list_expired');
}
function password_policy_send_login($account = NUL) {
global $base_url;
$from = variable_get('site_mail', ini_get('sendmail_from'));
$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 = array(
'From' => $from,
'Reply-to' => $from,
'X-Mailer' => 'Drupal',
'Return-path' => $from,
'Errors-to' => $from,
);
$mail_success = drupal_mail('password-policy-send-login', $account->mail, $subject, $body, $from, $headers);
if ($mail_success) {
watchdog('password_policy', t('Password reset instructions mailed to %name at %email.', array(
'%name' => $account->name,
'%email' => $account->mail,
)));
drupal_set_message(t('Further instructions have been sent to %name e-mail address.', array(
'%name' => $account->name,
)));
}
else {
watchdog('password_policy', t('Error mailing password reset instructions to %name at %email.', array(
'%name' => $account->name,
'%email' => $account->mail,
)), WATCHDOG_ERROR);
drupal_set_message(t('Unable to send mail. Please contact the site admin.'));
}
}
function password_policy_cron() {
$constraint = password_policy_load_active_policy();
if ($constraint) {
$expiration = $constraint
->getExpiration();
$warnings = explode(',', $constraint
->getWarning());
if (!empty($expiration)) {
$accounts = array();
$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_enabled($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) {
continue;
}
if ($time > $start_period && $time < $end_period) {
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 = array(
'From' => $from,
'Reply-to' => $from,
'X-Mailer' => 'Drupal',
'Return-path' => $from,
'Errors-to' => $from,
);
$mail_success = drupal_mail('password-policy-cron-warning', $account->mail, $subject, $body, $from, $headers);
if ($mail_success) {
watchdog('password_policy', t('Password expiration warning mailed to %username at %email.', array(
'%username' => $account->name,
'%email' => $account->mail,
)));
}
else {
watchdog('password_policy', t('Error mailing password expiration warning to %username at %email.', array(
'%username' => $account->name,
'%email' => $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);
}
}
}
}
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' => $account->name,
)), WATCHDOG_NOTICE, l(t('edit'), "user/{$account->uid}/edit"));
}
}
}
}
}
function _password_policy_mail_text($messageid, $variables = array()) {
if ($admin_setting = variable_get("password_policy_mail_{$messageid}", '')) {
return strtr($admin_setting, $variables);
}
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);
}
}
}
function _password_policy_enabled($expiration_seconds = 0) {
$result = db_query_range("SELECT * FROM {password_policy} WHERE enabled = 1 ORDER BY enabled DESC", 0, 1);
if ($row = db_fetch_object($result)) {
$policy_enabled = $row->created;
}
if (variable_get('password_policy_begin', 0) == 1) {
$policy_enabled -= $expiration_seconds;
}
return $policy_enabled;
}