diff --git a/README.txt b/README.txt
new file mode 100644
index 0000000..1a14bc2
--- /dev/null
+++ b/README.txt
@@ -0,0 +1,18 @@
+User Delete
+-----------
+Provide account cancellation methods and API to provide the same functionalty
+as Drupal 7 for cancelling accounts.
+
+Note: As of January 8, 2009 - 09:44 the issue is marked as fixed and commited
+to Drupal 7, see http://drupal.org/node/8#comment-1188824
+
+@author
+ilo <inaki.lopez@gmail.com>
+
+@TASK: split forms into user_delete.pages.inc
+@TASK: move batch to user_delete.admin.inc
+@TASK: complete the node/comments/whatever batch processes
+@TODO: select to use status_canceled or status_deleted. Now status canceled is
+       being used.
+@TODO: solve placeholders to tokens conversion during upgrade path.
+@TODO: what about other modules? triggers? track? profile?
diff --git a/user_delete.admin.inc b/user_delete.admin.inc
deleted file mode 100644
index e48952a..0000000
--- a/user_delete.admin.inc
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-
-/**
- * @file
- * User delete - Administration page
- *
- * @author
- * ilo <inaki.lopez@gmail.com>
- */
-
-/**
- * Administrative settings page
- *
- * @return array
- *   a form array
- */
-function user_delete_settings() {
-
-  // These email tokens are shared for all settings, so just define
-  // the list once to help ensure they stay in sync.
-  $email_token_help = t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !date, !login_uri, !edit_uri, !login_url.';
-
-  $form['registration_cancellation'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Settings for account cancellation'),
-  );
-
-  $form['registration_cancellation']['user_cancel_method'] = array(
-    '#type' => 'item',
-    '#title' => t('When cancelling a user account'),
-    '#description' => t('Users with the %select-cancel-method or %administer-users <a href="@permissions-url">permissions</a> can override this default method.', array('%select-cancel-method' => t('Select method for cancelling account'), '%administer-users' => t('Administer users'), '@permissions-url' => url('admin/user/permissions'))),
-  );
-  
-  $form['registration_cancellation']['user_cancel_method'] += user_delete_cancel_methods();
-  foreach (element_children($form['registration_cancellation']['user_cancel_method']) as $element) {
-    // Remove all account cancellation methods that have #access defined, as
-    // those cannot be configured as default method.
-    if (isset($form['registration_cancellation']['user_cancel_method'][$element]['#access'])) {
-      $form['registration_cancellation']['user_cancel_method'][$element]['#access'] = FALSE;
-    }
-    // Remove the description (only displayed on the confirmation form).
-    else {
-      unset($form['registration_cancellation']['user_cancel_method'][$element]['#description']);
-    }
-  }
-
-  $form['email_cancel_confirm'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Account cancellation confirmation'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#description' => t('Edit the e-mail messages sent to users when they attempt to cancel their accounts.') . ' ' . $email_token_help,
-  );
-  $form['email_cancel_confirm']['user_mail_cancel_confirm_subject'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Subject'),
-    '#default_value' => _user_delete_mail_text('cancel_confirm_subject'),
-    '#maxlength' => 180,
-  );
-  $form['email_cancel_confirm']['user_mail_cancel_confirm_body'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Body'),
-    '#default_value' => _user_delete_mail_text('cancel_confirm_body'),
-    '#rows' => 3,
-  );
-
-  $form['email_canceled'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Account canceled'),
-    '#collapsible' => TRUE,
-    '#collapsed' => TRUE,
-    '#description' => t('Enable and edit e-mail messages sent to users when their accounts are canceled.') . ' ' . $email_token_help,
-  );
-  $form['email_canceled']['user_mail_status_canceled_notify'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Notify user when account is canceled.'),
-    '#default_value' => variable_get('user_mail_status_canceled_notify', FALSE),
-  );
-  $form['email_canceled']['user_mail_status_canceled_subject'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Subject'),
-    '#default_value' => _user_delete_mail_text('status_canceled_subject'),
-    '#maxlength' => 180,
-  );
-  $form['email_canceled']['user_mail_status_canceled_body'] = array(
-    '#type' => 'textarea',
-    '#title' => t('Body'),
-    '#default_value' => _user_delete_mail_text('status_canceled_body'),
-    '#rows' => 3,
-  );
-
-  return system_settings_form($form);
-}
-
-/**
- * Return subject and body strings for default _user_mail_notify function.
- * @param <type> $key
- * @param <type> $language
- * @param <type> $variables
- * @return <type>
- */
-function _user_delete_mail_text($key, $language = NULL, $variables = array()) {
-  $langcode = isset($language) ? $language->language : NULL;
-
-  if ($admin_setting = variable_get('user_mail_'. $key, FALSE)) {
-    // An admin setting overrides the default string.
-    return strtr($admin_setting, $variables);
-  }
-  else {
-    // No override, return default string.
-    switch ($key) {
-      case 'cancel_confirm_subject':
-        return t('Account cancellation request for !username at !site', $variables, $langcode);
-        break;
-      case 'cancel_confirm_body':
-        return t("!username,\n\nA request to cancel your account has been made at !site.\n\nYou may now cancel your account on !uri_brief by clicking this link or copying and pasting it into your browser:\n\n!cancelurl\n\nNOTE: The cancellation of your account is not reversible.\n\nThis link expires in one day and nothing will happen if it is not used.\n\n\n--  !site team", $variables, $langcode);
-      case 'status_canceled_subject':
-        return t('Account details for !username at !site (canceled)', $variables, $langcode);
-      case 'status_canceled_body':
-        return t("!username,\n\nYour account on !site has been canceled.\n\n\n--  !site team", $variables, $langcode);
-        break;
-    }
-  }
-
-}
diff --git a/user_delete.info b/user_delete.info
index 850abb1..11148c4 100644
--- a/user_delete.info
+++ b/user_delete.info
@@ -1,3 +1,3 @@
-name         = User delete
-description  = "Backport of D7 account cancellation API and code."
+name = User delete
+description = Backport of D7 account cancellation API and code.
 core = 6.x
diff --git a/user_delete.install b/user_delete.install
index 47dba97..a7b1293 100644
--- a/user_delete.install
+++ b/user_delete.install
@@ -3,7 +3,6 @@
 /**
  * @file
  * User delete - Installation routines
- *
  */
 
 /**
@@ -23,7 +22,12 @@ function user_delete_install() {
 
 /**
  * Implementation of hook_uninstall().
- * @TODO: Not yet implemented.
  */
 function user_delete_uninstall() {
-}
\ No newline at end of file
+  variable_del('user_cancel_method');
+  variable_del('user_mail_cancel_confirm_body');
+  variable_del('user_mail_cancel_confirm_subject');
+  variable_del('user_mail_status_canceled_body');
+  variable_del('user_mail_status_canceled_notify');
+  variable_del('user_mail_status_canceled_subject');
+}
diff --git a/user_delete.module b/user_delete.module
index b413645..821d8df 100644
--- a/user_delete.module
+++ b/user_delete.module
@@ -2,23 +2,7 @@
 
 /**
  * @file
- * Provide account cancellation methods and API to provide the same functionalty
- * as Drupal 7 for cancelling accounts.
- *
- * Note: As of January 8, 2009 - 09:44 the issue is marked as fixed.
- * And commited to Drupal 7, see http://drupal.org/node/8#comment-1188824
- *
- * @author
- * ilo <inaki.lopez@gmail.com>
- *
- * @TASK: split forms into user_delete.pages.inc
- * @TASK: move batch to user_delete.admin.inc
- * @TASK: complete the node/comments/whatever batch processes
- * @TODO: select to use status_canceled or status_deleted. Now status cancelled
- *        is being used.
- * @TODO: solve placeholders to tokens conversion during upgrade path.
- * @TODO: what about other modules? triggers? track? profile?
-
+ * Allow user accounts to be deleted.
  */
 
 /**
@@ -32,20 +16,13 @@ function user_delete_perm() {
  * Implementation of hook_menu().
  */
 function user_delete_menu() {
-  $items['admin/user/user_delete'] = array(
-    'title' => 'User delete',
-    'description' => "Configure the user delete methods.",
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('user_delete_settings'),
-    'access arguments' => array('administer users'),
-    'file' => 'user_delete.admin.inc',
-  );
   $items['user/%user/delete/confirm/%/%'] = array(
     'title' => 'Confirm account cancellation',
-    'page callback' => 'user_delete_cancel_confirm',
+    'page callback' => 'user_delete_cancel_confirm_form',
     'page arguments' => array(1, 4, 5),
-    'access callback' => 'user_delete_access',
+    'access callback' => 'user_delete_delete_access',
     'access arguments' => array(1),
+    'file' => 'user_delete.pages.inc',
   );
   return $items;
 }
@@ -54,7 +31,7 @@ function user_delete_menu() {
  * Create a custom acces control function to verify if a user has access to the
  * user/%/delete menu entry.
  */
-function user_delete_access($account) {
+function user_delete_delete_access($account) {
   global $user;
   return (user_access('administer users') || (user_access('cancel account') && $account->uid == $user->uid));
 }
@@ -65,7 +42,7 @@ function user_delete_access($account) {
  * Take control of user/%/delete form and set the new access callback.
  */
 function user_delete_menu_alter(&$callbacks) {
-  $callbacks['user/%user/delete']['access callback'] = 'user_delete_access';
+  $callbacks['user/%user/delete']['access callback'] = 'user_delete_delete_access';
   $callbacks['user/%user/delete']['access arguments'] = array(1);
   $callbacks['user/%user/delete']['type'] = MENU_CALLBACK;
 }
@@ -84,12 +61,12 @@ function user_delete_form_alter(&$form, $form_state, $form_id) {
   global $user;
 
   if ($form_id == 'user_admin_account') {
-    // Change title to show Cancel instead of delete
+    // Change title to show Cancel instead of delete.
     $form['options']['operation']['#options']['delete'] = t('Cancel the selected accounts');
   }
 
   if ($form_id == 'user_profile_form') {
-    // Replace 'Delete' button label with 'Cancel account'
+    // Replace 'Delete' button label with 'Cancel account'.
     if (user_access('administer users') || (user_access('cancel account') && $form['#uid'] == $user->uid)) {
       $form['delete'] = array(
         '#type' => 'submit',
@@ -98,20 +75,19 @@ function user_delete_form_alter(&$form, $form_state, $form_id) {
         '#submit' => array('user_edit_delete_submit'),
       );
     }
-    /*
+    /**
      * There are some reasons to keep this check commented. There are modules
      * that already protect this button to appear, and Drupal 6 has not an 
      * special administrators group by default that can take control of the 
      * site if uid 1 is removed.  
      */
-    //if ($user->uid == 1) {
-    //  unset($form['delete']);
-    //}
+    if ($user->uid == 1) {
+     unset($form['delete']);
+    }
   }
 
-  // Take control of the user multiple delete confirmation form
+  // Take control of the user multiple delete confirmation form.
   if ($form_id == 'user_multiple_delete_confirm') {
-
     drupal_set_title(t('Are you sure you want to cancel these user accounts?'));
 
     $form['user_cancel_method'] = array(
@@ -148,12 +124,10 @@ function user_delete_form_alter(&$form, $form_state, $form_id) {
     $form['actions']['submit']['#value'] = t('Cancel accounts');
 
     $form['#submit'] = array('user_delete_multiple_confirm_submit');
-
   }
 
-  // Take control of the user delete confirmation form
+  // Take control of the user delete confirmation form.
   if ($form_id == 'user_confirm_delete') {
-
     $account = $form['_account']['#value'];
 
     // Display account cancellation method selection, if allowed.
@@ -203,8 +177,8 @@ function user_delete_form_alter(&$form, $form_state, $form_id) {
       }
     }
     else {
-      // The radio button #description is used as description for the confirmation
-      // form.
+      // The radio button #description is used as description for the
+      // confirmation form.
       foreach (element_children($form['user_cancel_method']) as $element) {
         if ($form['user_cancel_method'][$element]['#default_value'] == $form['user_cancel_method'][$element]['#return_value']) {
           $description = $form['user_cancel_method'][$element]['#description'];
@@ -226,9 +200,125 @@ function user_delete_form_alter(&$form, $form_state, $form_id) {
     $form['actions']['submit']['#value'] = t('Cancel account');
 
     $form['#submit'] = array('user_delete_confirm_form_submit');
+  }
+}
+
+/**
+ * Implementation of hook_form_FORM_ID_alter for user_admin_settings.
+ */
+function user_delete_form_user_admin_settings_alter(&$form, &$form_state) {
+  // These email tokens are shared for all settings, so just define the list
+  // once to help ensure they stay in sync.
+  $email_token_help = t('Available variables are:') .' !username, !site, !password, !uri, !uri_brief, !mailto, !date, !login_uri, !edit_uri, !login_url.';
+
+  $form['registration_cancellation'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Settings for account cancellation'),
+  );
+
+  $form['registration_cancellation']['user_cancel_method'] = array(
+    '#type' => 'item',
+    '#title' => t('When cancelling a user account'),
+    '#description' => t('Users with the %select-cancel-method or %administer-users <a href="@permissions-url">permissions</a> can override this default method.', array('%select-cancel-method' => t('Select method for cancelling account'), '%administer-users' => t('Administer users'), '@permissions-url' => url('admin/user/permissions'))),
+  );
+  
+  $form['registration_cancellation']['user_cancel_method'] += user_delete_cancel_methods();
+  foreach (element_children($form['registration_cancellation']['user_cancel_method']) as $element) {
+    // Remove all account cancellation methods that have #access defined, as
+    // those cannot be configured as default method.
+    if (isset($form['registration_cancellation']['user_cancel_method'][$element]['#access'])) {
+      $form['registration_cancellation']['user_cancel_method'][$element]['#access'] = FALSE;
+    }
 
+    // Remove the description (only displayed on the confirmation form).
+    else {
+      unset($form['registration_cancellation']['user_cancel_method'][$element]['#description']);
+    }
   }
 
+  $form['email_cancel_confirm'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Account cancellation confirmation'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#description' => t('Edit the e-mail messages sent to users when they attempt to cancel their accounts.') . ' ' . $email_token_help,
+  );
+  $form['email_cancel_confirm']['user_mail_cancel_confirm_subject'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Subject'),
+    '#default_value' => _user_delete_mail_text('cancel_confirm_subject'),
+    '#maxlength' => 180,
+  );
+  $form['email_cancel_confirm']['user_mail_cancel_confirm_body'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Body'),
+    '#default_value' => _user_delete_mail_text('cancel_confirm_body'),
+    '#rows' => 3,
+  );
+
+  $form['email_canceled'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Account canceled'),
+    '#collapsible' => TRUE,
+    '#collapsed' => TRUE,
+    '#description' => t('Enable and edit e-mail messages sent to users when their accounts are canceled.') . ' ' . $email_token_help,
+  );
+  $form['email_canceled']['user_mail_status_canceled_notify'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Notify user when account is canceled.'),
+    '#default_value' => variable_get('user_mail_status_canceled_notify', FALSE),
+  );
+  $form['email_canceled']['user_mail_status_canceled_subject'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Subject'),
+    '#default_value' => _user_delete_mail_text('status_canceled_subject'),
+    '#maxlength' => 180,
+  );
+  $form['email_canceled']['user_mail_status_canceled_body'] = array(
+    '#type' => 'textarea',
+    '#title' => t('Body'),
+    '#default_value' => _user_delete_mail_text('status_canceled_body'),
+    '#rows' => 3,
+  );
+
+  // Make sure the buttons display after the new options.
+  if (!isset($form['buttons']['#weight'])) {
+    $form['buttons']['#weight'] = 100;
+  }
+}
+
+/**
+ * Return subject and body strings for default _user_mail_notify function.
+ *
+ * @param string $key
+ * @param string $language
+ * @param array $variables
+ *
+ * @return string
+ */
+function _user_delete_mail_text($key, $language = NULL, $variables = array()) {
+  $langcode = isset($language) ? $language->language : NULL;
+
+  if ($admin_setting = variable_get('user_mail_'. $key, FALSE)) {
+    // An admin setting overrides the default string.
+    return strtr($admin_setting, $variables);
+  }
+  else {
+    // No override, return default string.
+    switch ($key) {
+      case 'cancel_confirm_subject':
+        return t('Account cancellation request for !username at !site', $variables, $langcode);
+
+      case 'cancel_confirm_body':
+        return t("!username,\n\nA request to cancel your account has been made at !site.\n\nYou may now cancel your account on !uri_brief by clicking this link or copying and pasting it into your browser:\n\n!cancelurl\n\nNOTE: The cancellation of your account is not reversible.\n\nThis link expires in one day and nothing will happen if it is not used.\n\n\n--  !site team", $variables, $langcode);
+
+      case 'status_canceled_subject':
+        return t('Account details for !username at !site (canceled)', $variables, $langcode);
+
+      case 'status_canceled_body':
+        return t("!username,\n\nYour account on !site has been canceled.\n\n\n--  !site team", $variables, $langcode);
+    }
+  }
 }
 
 /**
@@ -246,7 +336,8 @@ function user_delete_multiple_confirm_submit($form, &$form_state) {
       if ($uid <= 1) {
         continue;
       }
-      // Prevent user administrators from deleting themselves without confirmation.
+      // Prevent user administrators from deleting themselves without
+      // confirmation.
       if ($uid == $user->uid) {
         $admin_form_state = $form_state;
         unset($admin_form_state['values']['user_cancel_confirm']);
@@ -261,7 +352,6 @@ function user_delete_multiple_confirm_submit($form, &$form_state) {
   $form_state['redirect'] = 'admin/user/user';
 }
 
-
 /**
  * Submit handler for the account cancellation confirm form.
  *
@@ -297,7 +387,7 @@ function user_delete_confirm_form_submit($form, &$form_state) {
 }
 
 /**
- * Cancel a user account. This function is the Drupal 6 version of user_cancel
+ * Cancel a user account. This function is the Drupal 6 version of user_cancel.
  *
  * Copied from Drupal 7 user.module, modified version for Drupal 6
  *
@@ -334,10 +424,10 @@ function user_delete_cancel($edit, $uid, $method) {
 
   // Modules use hook_user_delete() to respond to deletion.
   // This is true in Drupal 7, but not in Drupal 6, so this check is commented.
-//  if ($method != 'user_cancel_delete') {
+  // if ($method != 'user_cancel_delete') {
     // Allow modules to add further sets to this batch.
     module_invoke_all('user_cancel', $edit, $account, $method);
-//  }
+  // }
 
   // Finish the batch and actually cancel the account.
   $batch = array(
@@ -378,7 +468,7 @@ function _user_delete_cancel($edit, $account, $method) {
 
     // This is default's Drupal 6 behavior, but comments are not really
     // anonymized, they are marked as not confirmed, so this needs some
-    // tweaks..
+    // tweaks.
     case 'user_cancel_reassign':
     case 'user_cancel_delete':
       // Send account canceled notification if option was checked.
@@ -399,37 +489,9 @@ function _user_delete_cancel($edit, $account, $method) {
 
   // Clear the cache for anonymous users.
   cache_clear_all();
-}
 
-/**
- * Menu callback; Cancel a user account via e-mail confirmation link.
- */
-function user_delete_cancel_confirm($account, $timestamp = 0, $hashed_pass = '') {
-  // Time out in seconds until cancel URL expires; 24 hours = 86400 seconds.
-  $timeout = 86400;
-  $current = time();
-  // Is it unserialized already?
-  $account->data = (is_array($account->data)) ? $account->data : unserialize($account->data);
-
-  // Basic validation of arguments.
-  if (isset($account->data['user_cancel_method']) && !empty($timestamp) && !empty($hashed_pass)) {
-    // Validate expiration and hashed password/login.
-    if ($timestamp <= $current && $current - $timestamp < $timeout && $account->uid && $timestamp >= $account->login && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login)) {
-      $edit = array(
-        'user_cancel_notify' => isset($account->data['user_cancel_notify']) ? $account->data['user_cancel_notify'] : variable_get('user_mail_status_canceled_notify', FALSE),
-      );
-      user_delete_cancel($edit, $account->uid, $account->data['user_cancel_method']);
-      // Since user_delete_cancel() is not invoked via Form API, batch processing needs
-      // to be invoked manually and should redirect to the front page after
-      // completion.
-      batch_process('');
-    }
-    else {
-      drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'));
-      drupal_goto("user/$account->uid/delete");
-    }
-  }
-  drupal_access_denied();
+  // Return the visitor to the homepage.
+  drupal_goto('<front>');
 }
 
 /**
@@ -498,8 +560,7 @@ function user_delete_cancel_url($account) {
  * Implements hook_mail_alter().
  *
  * We have no change to create the !cancelurl placeholder, so we need to keep
- * track of these emails and replace that string with a valid cancellation
- * url
+ * track of these emails and replace that string with a valid cancellation URL.
  */
 function user_delete_mail_alter(&$message) {
   if ($message['id'] == 'user_cancel_confirm') {
@@ -509,7 +570,8 @@ function user_delete_mail_alter(&$message) {
         $message['body'][$id] = str_replace('!cancelurl', $user_cancel_url, $message['body'][$id]);
       }
     }
-    else {  // Some modules use the body as a string, erroneously.
+    else {
+      // Some modules use the body as a string, erroneously.
       $message['body'][$id] = str_replace('!cancelurl', user_delete_cancel_url($message['params']['account']) , $message['body']);
     }
   }
@@ -536,18 +598,21 @@ function user_delete_user_cancel($edit, $account, $method) {
     // This method has nothing to do with content.
     case 'user_cancel_block':
       break;
+
     // Take all content and unpublish.
     case 'user_cancel_block_unpublish':
       user_delete_node_cancel($edit, $account, $method);
       user_delete_comment_cancel($edit, $account, $method);
-      //@TODO: update poll_vote to uid -> 0
+      // @TODO: update poll_vote to uid -> 0
       break;
+
     // The user will be deleted, reassign all content to uid 0.
     case 'user_cancel_reassign':
       user_delete_node_cancel($edit, $account, $method);
       user_delete_comment_cancel($edit, $account, $method);
       //@TODO: update poll_vote to uid -> 0
       break;
+
     // In this case the user and all content will really be deleted.
     case 'user_cancel_delete':
       user_delete_node_cancel($edit, $account, $method);
@@ -556,8 +621,6 @@ function user_delete_user_cancel($edit, $account, $method) {
   }
 }
 
-
-
 /**
  * Mimics hook_user_cancel() for node module.
  */
@@ -572,6 +635,7 @@ function user_delete_node_cancel($edit, $account, $method) {
       };
       user_delete_node_mass_update($nodes, array('status' => 0));
       break;
+
     case 'user_cancel_reassign':
       // Anonymize nodes (current revisions).
       $result = db_query("SELECT nid FROM {node} WHERE uid = %d", $account->uid);
@@ -585,6 +649,7 @@ function user_delete_node_cancel($edit, $account, $method) {
       // Anonymize history
       db_query("UPDATE {history} SET uid = 0 WHERE uid = %d", $account->uid);
       break;
+
     case 'user_cancel_delete':
       // Anonymize nodes (current revisions).
       $result = db_query("SELECT nid FROM {node} WHERE uid = %d", $account->uid);
@@ -598,22 +663,22 @@ function user_delete_node_cancel($edit, $account, $method) {
 }
 
 /**
- * Make mass update of nodes, changing all nodes in the $nodes array
- * to update them with the field values in $updates.
+ * Make mass update of nodes, changing all nodes in the $nodes array to update
+ * them with the field values in $updates.
  *
- * IMPORTANT NOTE: This function is intended to work when called
- * from a form submit handler. Calling it outside of the form submission
- * process may not work correctly.
+ * IMPORTANT NOTE: This function is intended to work when called from a form
+ * submit handler. Calling it outside of the form submission process may not
+ * work correctly.
  *
  * @param array $nodes
  *   Array of node nids to update.
  * @param array $updates
- *   Array of key/value pairs with node field names and the
- *   value to update that field to.
+ *   Array of key/value pairs with node field names and the value to update that
+ *   field to.
  */
 function user_delete_node_mass_update($nodes, $updates) {
-  // We use batch processing to prevent timeout when updating a large number
-  // of nodes.
+  // We use batch processing to prevent timeout when updating a large number of
+  // nodes.
   if (count($nodes) > 10) {
     $batch = array(
       'operations' => array(
@@ -625,8 +690,8 @@ function user_delete_node_mass_update($nodes, $updates) {
       // 'Remaining x of y operations' message will be confusing here.
       'progress_message' => '',
       'error_message' => t('The node update process has encountered an error.'),
-      // The operations do not live in the .module file, so we need to
-      // tell the batch engine which file to load before calling them.409
+      // The operations do not live in the .module file, so we need to tell the
+      // batch engine which file to load before calling them.409
       'file' => drupal_get_path('module', 'node') .'/node.admin.inc',
     );
     batch_set($batch);
@@ -652,7 +717,7 @@ function _user_delete_node_mass_update_helper($nid, $updates) {
 }
 
 /**
- * Node Mass Update Batch operation
+ * Node Mass Update Batch operation.
  */
 function _user_delete_node_mass_update_batch_process($nodes, $updates, &$context) {
   if (!isset($context['sandbox']['progress'])) {
@@ -675,8 +740,8 @@ function _user_delete_node_mass_update_batch_process($nodes, $updates, &$context
     $context['sandbox']['progress']++;
   }
 
-  // Inform the batch engine that we are not finished,
-  // and provide an estimation of the completion level we reached.
+  // Inform the batch engine that we are not finished, and provide an
+  // estimation of the completion level we reached.
   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
     $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
   }
@@ -699,26 +764,19 @@ function _user_delete_node_mass_update_batch_finished($success, $results, $opera
 
 // Node cancel batch process end
 
-
-
-
-
-
-
-
 /**
  * Make mass deletion of nodes.
  *
- * IMPORTANT NOTE: This function is intended to work when called
- * from a form submit handler. Calling it outside of the form submission
- * process may not work correctly.
+ * IMPORTANT NOTE: This function is intended to work when called from a form
+ * submit handler. Calling it outside of the form submission process may not
+ * work correctly.
  *
  * @param array $nodes
  *   Array of node nids to update.
  */
 function user_delete_node_mass_delete($nodes) {
-  // We use batch processing to prevent timeout when updating a large number
-  // of nodes.
+  // We use batch processing to prevent timeout when updating a large number of
+  // nodes.
   if (count($nodes) > 10) {
     $batch = array(
       'operations' => array(
@@ -730,8 +788,8 @@ function user_delete_node_mass_delete($nodes) {
       // 'Remaining x of y operations' message will be confusing here.
       'progress_message' => '',
       'error_message' => t('The delete has encountered an error.'),
-      // The operations do not live in the .module file, so we need to
-      // tell the batch engine which file to load before calling them.409
+      // The operations do not live in the .module file, so we need to tell the
+      // batch engine which file to load before calling them.
       'file' => drupal_get_path('module', 'node') .'/node.admin.inc',
     );
     batch_set($batch);
@@ -753,12 +811,10 @@ function _user_delete_node_mass_delete_helper($nid) {
   return $node;
 }
 
-
 /**
  * Delete a node without user access.
  */
 function _user_delete_node_delete($nid) {
-
   // Clear the cache before the load, so if multiple nodes are deleted, the
   // memory will not fill up with nodes (possibly) already removed.
   $node = node_load($nid, NULL, TRUE);
@@ -766,7 +822,7 @@ function _user_delete_node_delete($nid) {
   db_query('DELETE FROM {node} WHERE nid = %d', $node->nid);
   db_query('DELETE FROM {node_revisions} WHERE nid = %d', $node->nid);
 
-  // Call the node-specific callback (if any):
+  // Call the node-specific callback (if any).
   node_invoke($node, 'delete');
   node_invoke_nodeapi($node, 'delete');
 
@@ -781,10 +837,8 @@ function _user_delete_node_delete($nid) {
   drupal_set_message(t('@type %title has been deleted.', array('@type' => node_get_types('name', $node), '%title' => $node->title)));
 }
 
-
-
 /**
- * Node Mass Delete Batch operation
+ * Node Mass Delete Batch operation.
  */
 function _user_delete_node_mass_delete_batch_process($nodes, &$context) {
   if (!isset($context['sandbox']['progress'])) {
@@ -807,8 +861,8 @@ function _user_delete_node_mass_delete_batch_process($nodes, &$context) {
     $context['sandbox']['progress']++;
   }
 
-  // Inform the batch engine that we are not finished,
-  // and provide an estimation of the completion level we reached.
+  // Inform the batch engine that we are not finished, and provide an
+  // estimation of the completion level we reached.
   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
     $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
   }
@@ -829,16 +883,6 @@ function _user_delete_node_mass_delete_batch_finished($success, $results, $opera
   }
 }
 
-
-
-
-
-
-
-
-
-
-
 /**
  * Comment related stuff
  */
@@ -859,6 +903,7 @@ function user_delete_comment_cancel($edit, $account, $method) {
       // @TODO: Also remove email and username..
       user_delete_comment_mass_update($cids, array('status' => 1));
       break;
+
     case 'user_cancel_reassign':
       // Anonymize comments.
       $result = db_query("SELECT cid FROM {comments} WHERE uid = %d", $account->uid);
@@ -868,6 +913,7 @@ function user_delete_comment_cancel($edit, $account, $method) {
       };
       user_delete_comment_mass_update($cids, array('uid' => 0));
       break;
+
     case 'user_cancel_delete':
       // delete comments.
       $result = db_query("SELECT cid FROM {comments} WHERE uid = %d", $account->uid);
@@ -877,12 +923,10 @@ function user_delete_comment_cancel($edit, $account, $method) {
       };
       user_delete_comment_mass_delete($cids);
   }
-}
-
+}
 
 // Comment batch process code start
 
-
 /**
  * Make mass update of comments, changing all comments in the $comments array
  * to update them with the field values in $updates.
@@ -898,8 +942,8 @@ function user_delete_comment_cancel($edit, $account, $method) {
  *   value to update that field to.
  */
 function user_delete_comment_mass_update($comments, $updates) {
-  // We use batch processing to prevent timeout when updating a large number
-  // of nodes.
+  // We use batch processing to prevent timeout when updating a large number of
+  // nodes.
   if (count($comments) > 10) {
     $batch = array(
       'operations' => array(
@@ -911,9 +955,9 @@ function user_delete_comment_mass_update($comments, $updates) {
       // 'Remaining x of y operations' message will be confusing here.
       'progress_message' => '',
       'error_message' => t('The update has encountered an error.'),
-      // The operations do not live in the .module file, so we need to
-      // tell the batch engine which file to load before calling them.409
-//      'file' => drupal_get_path('module', 'node') .'/node.admin.inc',
+      // The operations do not live in the .module file, so we need to tell the
+      // batch engine which file to load before calling them.
+      // 'file' => drupal_get_path('module', 'node') .'/node.admin.inc',
     );
     batch_set($batch);
   }
@@ -926,7 +970,7 @@ function user_delete_comment_mass_update($comments, $updates) {
 }
 
 /**
- * comment Mass Update - helper function.
+ * Comment Mass Update - helper function.
  */
 function _user_delete_comment_mass_update_helper($cid, $updates) {
   $comment = _user_delete_comment_load($cid);
@@ -937,17 +981,20 @@ function _user_delete_comment_mass_update_helper($cid, $updates) {
   return $comment;
 }
 
-// A copy of comment_get without user acces and only for existing comments.
-
+/**
+ * A copy of comment_get without user acces and only for existing comments.
+ */
 function _user_delete_comment_load($cid) {
   $comment = db_fetch_object(db_query("SELECT * FROM {comments} WHERE cid = %d", $cid));
   return $comment;
 }
 
-// A copy of comment_save without user access and only for existing comments.
-// @TODO: Should comment be converted to array before calling the update hook?
+/**
+ * A copy of comment_save without user access and only for existing comments.
+ *
+ * @TODO: Should comment be converted to array before calling the update hook?
+ */
 function _user_delete_comment_save($comment) {
-
   drupal_write_record('comments', $comment, 'cid');
 
   foreach (module_implements('comment') as $name) {
@@ -965,7 +1012,7 @@ function _user_delete_comment_save($comment) {
 }
 
 /**
- * comment Mass Update Batch operation
+ * Comment Mass Update Batch operation.
  */
 function _user_delete_comment_mass_update_batch_process($comments, $updates, &$context) {
   if (!isset($context['sandbox']['progress'])) {
@@ -988,8 +1035,8 @@ function _user_delete_comment_mass_update_batch_process($comments, $updates, &$c
     $context['sandbox']['progress']++;
   }
 
-  // Inform the batch engine that we are not finished,
-  // and provide an estimation of the completion level we reached.
+  // Inform the batch engine that we are not finished, and provide an
+  // estimation of the completion level we reached.
   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
     $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
   }
@@ -1010,21 +1057,19 @@ function _user_delete_comment_mass_update_batch_finished($success, $results, $op
   }
 }
 
-
-
 /**
  * Make mass delete of comments
  *
- * IMPORTANT NOTE: This function is intended to work when called
- * from a form submit handler. Calling it outside of the form submission
- * process may not work correctly.
+ * IMPORTANT NOTE: This function is intended to work when called from a form
+ * submit handler. Calling it outside of the form submission process may not
+ * work correctly.
  *
  * @param array $comments
  *   Array of comment cids to update.
  */
 function user_delete_comment_mass_delete($comments) {
-  // We use batch processing to prevent timeout when updating a large number
-  // of nodes.
+  // We use batch processing to prevent timeout when updating a large number of
+  // nodes.
   if (count($comments) > 10) {
     $batch = array(
       'operations' => array(
@@ -1036,9 +1081,9 @@ function user_delete_comment_mass_delete($comments) {
       // 'Remaining x of y operations' message will be confusing here.
       'progress_message' => '',
       'error_message' => t('The delete has encountered an error.'),
-      // The operations do not live in the .module file, so we need to
-      // tell the batch engine which file to load before calling them.409
-//      'file' => drupal_get_path('module', 'node') .'/node.admin.inc',
+      // The operations do not live in the .module file, so we need to tell the
+      // batch engine which file to load before calling them.
+      // 'file' => drupal_get_path('module', 'node') .'/node.admin.inc',
     );
     batch_set($batch);
   }
@@ -1051,7 +1096,7 @@ function user_delete_comment_mass_delete($comments) {
 }
 
 /**
- * comment Mass Update - helper function.
+ * Comment Mass Update - helper function.
  */
 function _user_delete_comment_mass_delete_helper($cid) {
   $comment = _user_delete_comment_load($cid);
@@ -1086,7 +1131,7 @@ function _user_delete_comment_delete_thread($comment) {
     }
   }
 
-  // Delete the comment's replies
+  // Delete the comment's replies.
   $result = db_query('SELECT c.*, u.name AS registered_name, u.uid FROM {comments} c INNER JOIN {users} u ON u.uid = c.uid WHERE pid = %d', $comment->cid);
   while ($comment = db_fetch_object($result)) {
     $comment->name = $comment->uid ? $comment->registered_name : $comment->name;
@@ -1095,7 +1140,7 @@ function _user_delete_comment_delete_thread($comment) {
 }
 
 /**
- * comment Mass Update Batch operation
+ * Comment Mass Update Batch operation.
  */
 function _user_delete_comment_mass_delete_batch_process($comments, &$context) {
   if (!isset($context['sandbox']['progress'])) {
@@ -1118,8 +1163,8 @@ function _user_delete_comment_mass_delete_batch_process($comments, &$context) {
     $context['sandbox']['progress']++;
   }
 
-  // Inform the batch engine that we are not finished,
-  // and provide an estimation of the completion level we reached.
+  // Inform the batch engine that we are not finished, and provide an
+  // estimation of the completion level we reached.
   if ($context['sandbox']['progress'] != $context['sandbox']['max']) {
     $context['finished'] = $context['sandbox']['progress'] / $context['sandbox']['max'];
   }
@@ -1139,4 +1184,3 @@ function _user_delete_comment_mass_delete_batch_finished($success, $results, $op
     drupal_set_message($message);
   }
 }
-
diff --git a/user_delete.pages.inc b/user_delete.pages.inc
new file mode 100644
index 0000000..1022cef
--- /dev/null
+++ b/user_delete.pages.inc
@@ -0,0 +1,36 @@
+<?php
+/**
+ * @file
+ * Non-admin pages for the User Delete module.
+ */
+
+/**
+ * Menu callback; Cancel a user account via e-mail confirmation link.
+ */
+function user_delete_cancel_confirm_form($account, $timestamp = 0, $hashed_pass = '') {
+  // Time out in seconds until cancel URL expires; 24 hours = 86400 seconds.
+  $timeout = 86400;
+  $current = time();
+  // Is it unserialized already?
+  $account->data = (is_array($account->data)) ? $account->data : unserialize($account->data);
+
+  // Basic validation of arguments.
+  if (isset($account->data['user_cancel_method']) && !empty($timestamp) && !empty($hashed_pass)) {
+    // Validate expiration and hashed password/login.
+    if ($timestamp <= $current && $current - $timestamp < $timeout && $account->uid && $timestamp >= $account->login && $hashed_pass == user_pass_rehash($account->pass, $timestamp, $account->login)) {
+      $edit = array(
+        'user_cancel_notify' => isset($account->data['user_cancel_notify']) ? $account->data['user_cancel_notify'] : variable_get('user_mail_status_canceled_notify', FALSE),
+      );
+      user_delete_cancel($edit, $account->uid, $account->data['user_cancel_method']);
+      // Since user_delete_cancel() is not invoked via Form API, batch
+      // processing needs to be invoked manually and should redirect to the
+      // front page after completion.
+      batch_process('');
+    }
+    else {
+      drupal_set_message(t('You have tried to use an account cancellation link that has expired. Please request a new one using the form below.'));
+      drupal_goto("user/$account->uid/delete");
+    }
+  }
+  drupal_access_denied();
+}
diff --git a/user_delete.test b/user_delete.test
index 0a67fb3..8ee2d64 100644
--- a/user_delete.test
+++ b/user_delete.test
@@ -39,5 +39,4 @@ class UserDeleteTest extends DrupalWebTestCase {
     // Create an admin user to configure user delete module.
     $this->admin = $this->drupalCreateUser(array('administer users'));
   }
-
 }
