Index: peek.admin.inc
===================================================================
RCS file: peek.admin.inc
diff -N peek.admin.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ peek.admin.inc	10 Sep 2008 01:48:11 -0000
@@ -0,0 +1,120 @@
+<?php
+// $Id$
+
+/**
+ * Menu callback for the peek settings form.
+ */
+function peek_admin_settings() {
+  $duration = (integer) variable_get('peek_duration', 3600);
+  $expiry = (integer) variable_get('peek_expiry', 3 * 24 * 3600);
+  $form['settings_general'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('Default settings'),
+  );
+  $form['settings_general']['peek_anonymous'] = array(
+    '#type' => 'checkbox',
+    '#title' => t('Anonymous Peeks'),
+    '#return_value' => 1,
+    '#default_value' => variable_get('peek_anonymous', 0),
+    '#description' => t("If checked, don't generate new users when generating a peek for a non-user, use anonymous access."),
+  );
+  $form['settings_general']['peek_duration'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Peek duration'),
+    '#default_value' => $duration / 3600,
+    '#size' => 15,
+    '#maxlength' => 10,
+    '#description' => t('Length of time in hours that the peek link remains valid after first access.'),
+    '#field_suffix' => t('hours')
+  );
+  $form['settings_general']['peek_expiry'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Peek expiry'),
+    '#default_value' => $expiry / (24 * 3600),
+    '#size' => 15,
+    '#maxlength' => 10,
+    '#description' => t('Length of time in days that the first access of the peek link remains valid.'),
+    '#field_suffix' => t('days'),
+  );
+  // Inject our won submit handler to convert the units.
+  $form['#submit'][] = 'peek_admin_settings_submit';
+  $form['#validate'][] = 'peek_admin_settings_validate';
+  return system_settings_form($form);
+}
+
+/**
+ * Form API callbacks to validate and submit the peek settings form.
+ */
+function peek_admin_settings_validate($form, &$form_state) {
+  if (!is_numeric($form_state['values']['peek_duration'])) {
+    form_set_error('peek_duration', t('You must set the peek duration to a number.'));
+  }
+  if (!is_numeric($form_state['values']['peek_expiry'])) {
+    form_set_error('peek_expiry', t('You must set the peek expiry to a number.'));
+  }
+}
+
+function peek_admin_settings_submit($form, &$form_state) {
+  // Convert the format and the let system_settings_form_submit() save them.
+  $form_state['values']['peek_duration'] = (integer) (3600 * $form_state['values']['peek_duration']);
+  $form_state['values']['peek_expiry'] = (integer) (24 * 3600 * $form_state['values']['peek_expiry']);
+}
+
+/**
+ * peek_peek
+ *
+ * Display full detail about a peek access times and provide an opportunity to delete it.
+ *
+ * @param $peek A peek object returned by peek_load().
+ */
+function peek_peek_form(&$form_state, $peek) {
+  $form['peek_info']['sid'] = array('#type' => 'value', '#name' => 'peek', '#value' => $peek->sid);
+  $form['peek_info']['peek'] = array('#type' => 'markup', '#value' => theme('table', array(), _peek_info($peek)));
+  $form['peek_info']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
+
+  $result = db_query('SELECT hostname, timestamp FROM {peek_access} WHERE sid = "%s"', $peek->sid);
+  $rows = array();
+  while ($p = db_fetch_array($result)) {
+    $p['timestamp'] = format_date($p['timestamp']);
+    $rows[] = $p;
+  }
+  if ($rows) {
+    $header = array('hostname', 'date/time');
+    $log = theme('table', $header, $rows);
+    $form['peek_log'] = array('#type' => 'fieldset', '#title' => t('Access log'));
+    $form['peek_log']['log'] = array('#type' => 'markup', '#value' => $log);
+  }
+  return $form;
+}
+
+function peek_peek_form_submit($form, &$form_state) {
+  $sid = $form_state['values']['sid'];
+
+  db_query("DELETE FROM {peek} WHERE sid = '%s'", $sid);
+  db_query("DELETE FROM {peek_access} WHERE sid = '%s'", $sid);
+
+  drupal_set_message(t('Peek deleted.'));
+  $form_state['redirect'] = 'admin/content/peek';
+}
+
+/**
+ * peek_content_administer
+ *
+ * Display a list of generated peeks and their status
+ *
+ */
+function peek_content_administer() {
+  $output = peek_table(array());
+  if (!$output) {
+    $output = t('No peeks');
+  }
+  return $output;
+}
+
+function peek_content_administer_unpeeked($params = NULL, $title = 'Peek Administration') {
+  $output = peek_table(array('first_access' => 0));
+  if (!$output) {
+    $output = t('No peeks');
+  }
+  return $output;
+}
\ No newline at end of file
Index: peek.info
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/peek/peek.info,v
retrieving revision 1.3
diff -u -p -r1.3 peek.info
--- peek.info	22 Aug 2008 02:56:40 -0000	1.3
+++ peek.info	10 Sep 2008 01:48:11 -0000
@@ -1,2 +1,4 @@
+; $Id$
 name = Peek
 description = "Peek generation and administration."
+core = 6.x
Index: peek.install
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/peek/peek.install,v
retrieving revision 1.1
diff -u -p -r1.1 peek.install
--- peek.install	22 Mar 2007 16:18:34 -0000	1.1
+++ peek.install	10 Sep 2008 01:48:11 -0000
@@ -1,5 +1,133 @@
 <?php
-// $Id: peek.install
+// $Id: peek.install$
+
+/**
+ * Implementation of hook_install().
+ */
+function peek_schema() {
+  $schema['peek'] = array(
+    'description' => t('Table tracking the peekable permissions for nodes.'),
+    'fields' => array(
+      'sid' => array(
+        'description' => t('TODO: XXX.'),
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+      'nid' => array(
+        'description' => t('TODO: XXX.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'uid' => array(
+        'description' => t('TODO: XXX.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'created' => array(
+        'description' => t('The Unix timestamp when the access was created.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'expires' => array(
+        'description' => t('The Unix timestamp when the access expires.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'duration' => array(
+        'description' => t('TODO: The Unix timestamp'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'first_access' => array(
+        'description' => t('The Unix timestamp when the access was first used.'),
+        'type' => 'int',
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+      'notify' => array(
+        'description' => t('TODO: XXX.'),
+        'type' => 'varchar',
+        'length' => 255,
+        'not null' => TRUE,
+        'default' => '',
+      ),
+    ),
+    'primary key' => array('sid'),
+    'indexes' => array(
+      'peek_nid'        => array('nid'),
+      'peek_uid'        => array('uid'),
+      'peek_created'    => array('created'),
+    ),
+  );
+
+  $schema['peek_access'] = array(
+    'fields' => array(
+      'peek_id' => array(
+        'description' => t('TODO: XXX.'),
+        'type' => 'serial',
+        'not null' => TRUE
+      ),
+      'sid' => array(
+        'description' => t('TODO: XXX.'),
+        'type' => 'varchar',
+        'length' => 64,
+        'not null' => TRUE,
+        'default' => ''
+      ),
+      'hostname' => array(
+        'description' => t('TODO: XXX.'),
+        'type' => 'varchar',
+        'length' => 128,
+      ),
+      'timestamp' => array(
+        'description' => t('The Unix timestamp when the access occurred.'),
+        'type' => 'int',
+      ),
+    ),
+    'primary key' => array('peek_id'),
+    'indexes' => array(
+      'peek_access_sid_timestamp' => array('sid', 'timestamp'),
+    ),
+  );
+
+  $schema['peekable'] = array(
+    'fields' => array(
+      'nid' => array(
+        'description' => t('TODO: XXX.'),
+        'type' => 'int',
+        'unsigned' => TRUE,
+        'not null' => TRUE,
+        'default' => 0,
+      ),
+    ),
+    'primary key' => array('nid'),
+  );
+
+  return $schema;
+}
+
+/**
+ * Implementation of hook_install().
+ */
+function peek_install() {
+  drupal_install_schema('peek');
+}
+
+/**
+ * Implementation of hook_uninstall().
+ */
+function peek_uninstall() {
+  drupal_uninstall_schema('peek');
+}
 
 function peek_update_1() {
   $ret = array();
@@ -33,47 +161,3 @@ function peek_update_2() {
   drupal_set_message("peek notification field added.");
   return $ret;
 }
-
-
-function peek_install() {
-  switch ($GLOBALS['db_type']) {
-    case 'mysql':
-    case 'mysqli':
-      db_query("create table if not exists {peek} (
-        sid varchar(64) NOT NULL default '',
-        nid int(10) unsigned NOT NULL default '0',
-        uid int(10) unsigned NOT NULL default '0',
-        created int(11) NOT NULL DEFAULT 0,
-        expires int(11) NOT NULL DEFAULT 0,
-        duration int(11) NOT NULL DEFAULT 0,
-        first_access int(11) NOT NULL DEFAULT 0,
-        notify varchar(255) NOT NULL default '',
-        PRIMARY KEY (sid),
-        KEY (nid),
-        KEY (uid),
-        KEY (created) 
-      );");
-
-      db_query("create table if not exists {peek_access} (
-        peek_id int(11) auto_increment,
-        sid varchar(64) NOT NULL default '',
-        hostname varchar(128),
-        timestamp int(11),
-        PRIMARY KEY (peek_id),
-        KEY (sid, timestamp)
-      );");
-
-      db_query("create table if not exists {peekable} (
-        nid int(10) unsigned NOT NULL default '0',
-        PRIMARY KEY (nid)
-      );");
-
-      break;
-
-    case 'pgsql':
-
-      die('TODO: pgsql');
-      break;
-  }
-  drupal_set_message("peek database tables created.");
-}
Index: peek.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/peek/peek.module,v
retrieving revision 1.2
diff -u -p -r1.2 peek.module
--- peek.module	22 Aug 2008 02:56:40 -0000	1.2
+++ peek.module	10 Sep 2008 01:48:12 -0000
@@ -5,51 +5,16 @@
  * @file
  *
  * This module provides a 'peek' option for nodes. That is, it can generate
- * temporary 'secret' urls that provide a view of a single node for a 
+ * temporary 'secret' urls that provide a view of a single node for a
  * restricted period of time, ignoring any existing permission restrictions
  * on the node.
  */
 
 /**
- * Implementation of hook_form_alter().
- *
- * Enable a 'peekability' radio selection in the workflow section of the node type configuration.
- * Enable a 'peekable' checkbox in the node edit form for relevant node types as per peekability settings.
- */
-
-function peek_form_alter($form_id, &$form) {
-  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
-    $form['workflow']['peek'] = array(
-      '#type' => 'radios',
-      '#title' => t('Peeks'),
-      '#default_value' => variable_get('peek_'. $form['#node_type']->type, 1),
-      '#options' => array(t('Disabled'), t('Optional, default off for new nodes'),t('Optional, default on for new nodes')),
-    );
-  }
-
-  if (isset($form['type'])) {
-    $node = $form['#node'];
-    $type_peekability = variable_get("peek_$node->type", 1);
-    if ($form['type']['#value'] .'_node_form' == $form_id && $type_peekability) {
-      $peekable_default = $node->peekable ? 1 : 
-        ($node->nid ? 0 : ((2 == $type_peekability) ? 1 : 0));
-      $form['options']['set_peekable'] = array(
-        '#type' => 'checkbox',
-        '#default_value' => $peekable_default,
-        '#access' => user_access('administer peeks'),
-        '#title' => t('Peekable'),
-        '#description' => t('Check this box to allow peeks to be generated for this node'),
-      );
-    }
-  }
-}
-
-
-/**
- * Implementation of hook_help().
+ * Implementation of hook_help() .
  */
-function peek_help($section) {
-  switch ($section) {
+function peek_help($path, $arg) {
+  switch ($path) {
     case 'admin/help#peek':
       return t('This module provides an extra tab for permission-enabled users that allow them to send emails to other users with a special permissioned url. This url will give the recipient an opportunity to get a \'peek\' of the corresponding node. The permission inherent in the url will last for a restricted period of time, and only gives access to that node. If the user already has view access to the node, the user will be switched to the normal node url. The module avoids the usual permission restrictions by providing access via a different url than the usual node.');
     case 'admin/modules#description':
@@ -59,150 +24,255 @@ function peek_help($section) {
 
 
 /**
- * Implementation of hook_menu().
+ * Implementation of hook_menu() .
  */
-function peek_menu($may_cache) {
+function peek_menu() {
   $items = array();
 
-  if ($may_cache) {
-    $items[] = array('path' => 'admin/settings/peek',
-      'title' => t('Peek configuration'),
-      'description' => t('Set default peek values.'),
-      'callback' => 'drupal_get_form',
-      'callback arguments' => array('peek_admin_settings'),
-      'access' => user_access('administer site configuration'),
-      'type' => MENU_NORMAL_ITEM);
-    $items[] = array(
-      'path' => 'admin/content/peek',
-      'title' => t('Peeks'),
-      'access' => user_access('administer peeks'),
-      'callback' => 'peek_content_administer',
-    );
-    $items[] = array(
-      'path' => 'admin/content/peek/all',
-      'title' => t('All'),
-      'weight' => -10,
-      'type' => MENU_DEFAULT_LOCAL_TASK,
-      'callback arguments' => array(),
-    );
-    $items[] = array(
-      'path' => 'admin/content/peek/unpeeked',
-      'title' => t('Unused peeks'),
-      'weight' => 1,
-      'callback' => 'peek_content_administer',
-      'type' => MENU_LOCAL_TASK,
-      'callback arguments' => array(array('first_access' => 0)),
-    );
-    $items[] = array(
-      'path' => 'admin/content/peek/peek',
-      'title' => t('Peek Detail'),
-      'callback' => 'drupal_get_form',
-      'callback arguments' => array('peek_peek_form'),
-      'type' => MENU_CALLBACK,
-    );
-    $items[] = array(
-      'path' => 'peek',
-      'title' => t('Node Peek'),
-      'access' => TRUE,
-      'type' => MENU_CALLBACK,
-      'callback' => 'peek_show'
-    );
+  $items['admin/settings/peek'] = array(
+    'title' => 'Peek configuration',
+    'description' => 'Set default peek values.',
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('peek_admin_settings'),
+    'access arguments' => array('administer site configuration'),
+    'file' => 'peek.admin.inc',
+    'type' => MENU_NORMAL_ITEM,
+  );
+
+  $items['admin/content/peek'] = array(
+    'title' => 'Peeks',
+    'access arguments' => array('administer peeks'),
+    'page callback' => 'peek_content_administer',
+    'file' => 'peek.admin.inc',
+  );
+  $items['admin/content/peek/all'] = array(
+    'title' => 'All',
+    'page arguments' => array(),
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+    'weight' => -10,
+  );
+  $items['admin/content/peek/unpeeked'] = array(
+    'title' => 'Unused peeks',
+    'access arguments' => array('administer peeks'),
+    'page callback' => 'peek_content_administer_unpeeked',
+    'file' => 'peek.admin.inc',
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 1,
+  );
+  $items['admin/content/peek/edit/%peek'] = array(
+    'title' => 'Peek Detail',
+    'access arguments' => array('administer peeks'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('peek_peek_form', 4),
+    'file' => 'peek.admin.inc',
+    'type' => MENU_CALLBACK,
+  );
+
+  //$uid, $timestamp, $sid
+  $items['peek/%user/%/%peek'] = array(
+    'title' => 'Node Peek',
+    'access callback' => 'peek_show_access',
+    'access arguments' => array(1, 2, 3),
+    'page callback' => 'peek_show',
+    'page arguments' => array(1, 2, 3),
+    'type' => MENU_CALLBACK,
+  );
+
+  $items['node/%node/peek'] = array(
+    'title' => 'Peek',
+    'access callback' => 'peek_node_page_access',
+    'access arguments' => array(1),
+    'page callback' => 'peek_node_page',
+    'page arguments' => array(1),
+    'file' => 'peek.pages.inc',
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 1,
+  );
+  $items['user/%user/peek'] = array(
+    'title' => 'Peeks',
+    'access arguments' => array('administer peeks'),
+    'page callback' => 'peek_user_page',
+    'page arguments' => array(1),
+    'file' => 'peek.pages.inc',
+    'type' => MENU_LOCAL_TASK,
+    'weight' => 1,
+  );
+
+  return $items;
+}
+
+/**
+ * Controls access to the peek pages.
+ *
+ * @param $account
+ * @param $timestamp
+ * @param $peek
+ */
+function peek_show_access($account, $timestamp, $peek) {
+  global $user;
+
+  $current = time();
+
+  if (!is_numeric($timestamp) || $timestamp > $current) {
+    watchdog('peek log', 'Invalid peek attempt: URL syntax', array(), WATCHDOG_WARNING);
+    return FALSE;
   }
-  elseif ( 'node' == arg(0) && is_numeric(arg(1)) ) {
-    $node = node_load(arg(1));
-    if ($node->peekable) {
-      $items[] = array(
-        'path' => 'node/'.arg(1).'/peek',
-        'title' => t('Peek'),
-        'access' => user_access('generate peeks'),
-        'type' => MENU_LOCAL_TASK,
-        'weight' => 1,
-        'callback' => 'drupal_get_form',
-        'callback arguments' => array('peek_generate_form',arg(1)),
-      );
-    }
-    elseif ($node->nodepeeks) {
-      $items[] = array(
-        'path' => 'node/'.arg(1).'/peek',
-        'title' => t('Peek'),
-        'access' => user_access('generate peeks'),
-        'type' => MENU_LOCAL_TASK,
-        'weight' => 1,
-        'callback' => 'peek_content_administer',
-        'callback arguments' => array(array('nid' => arg(1)),'Peek Leftovers'),
-      );
+
+  // user security check
+  if (!$account->uid && !variable_get('peek_anonymous', 0)) {
+    watchdog('peek log', 'Invalid peek attempt: invalid user', array(), WATCHDOG_WARNING);
+    return drupal_access_denied();
+  }
+  elseif ($user->uid) {
+    if ($account->uid != $user->uid) {
+      watchdog('peek log', 'Invalid peek attempt: wrong user', array(), WATCHDOG_WARNING);
+      return drupal_access_denied();
     }
-  } 
-  elseif ( 'user' == arg(0) && is_numeric(arg(1)) ) {
-    $items[] = array(
-      'path' => 'user/'.arg(1).'/peek',
-      'title' => t('Peeks'),
-      'access' => user_access('administer peeks'),
-      'type' => MENU_LOCAL_TASK,
-      'weight' => 1,
-      'callback' => 'peek_content_administer',
-      'callback arguments' => array(array('uid' => arg(1)),''),
-    );
   }
-  return $items;
-}
+  elseif ($account->uid)  { // switch to this user
+    $user = $account;
+    cache_clear_all($account->uid .':', 'cache_menu', TRUE);
+    drupal_goto($_GET['q']);
+  }
+
+  // peek security checks
+  if ($timestamp != $peek->created) {
+    watchdog('peek log', 'Invalid peek attempt: incorrect timestamp', array(), WATCHDOG_WARNING);
+    return FALSE;
+  }
+
+  $invalid = _peek_status($peek, $current);
+  if (count($invalid)) {
+    watchdog('peek log', 'Invalid peek attempt: %invalid', array('%invalid' => implode(', ', $invalid)), WATCHDOG_WARNING);
+    return FALSE;
+  }
 
+  return TRUE;
+}
 
 /**
- * Implementation of hook_user().
+ * Control access to the peek tab on nodes.
+ *
+ * @param unknown_type $node
+ * @return unknown
  */
+function peek_node_page_access($node) {
+  return (user_access('generate peeks') && ($node->peekable || $node->nodepeeks));
+}
 
+function peek_theme() {
+  return array(
+    'peek_body' => array(
+      'arguments' => array('path' => NULL, 'node' => NULL),
+    ),
+    'peek_notify_email' => array(
+      'arguments' => array('peek' => NULL),
+    ),
+    'peek_subject' => array(
+      'arguments' => array('node' => NULL),
+    ),
+  );
+}
+
+/**
+ * Implementation of hook_user() .
+ */
 function peek_user($op, &$edit, &$user) {
   if ($op == 'delete') {
-    $sids = array();
-    $result = db_query('SELECT sid FROM {peek} p WHERE uid = %d',$user->uid);
-    while($p = db_fetch_array($result)) {
-      $sids[] = $p['sid'];
-    }
-    if (count($sids)) {
-      db_query('DELETE FROM {peek} WHERE sid in ("'.implode('","',$sids).'")');
-      db_query('DELETE FROM {peek_access} WHERE sid in ("'.implode('","',$sids).'")');
+    $result = db_query('SELECT sid FROM {peek} p WHERE uid = %d', $user->uid);
+    while ($p = db_fetch_array($result)) {
+      db_query("DELETE FROM {peek} WHERE sid = '%s'", $p->sid);
+      db_query("DELETE FROM {peek_access} WHERE sid = '%s'", $p->sid);
     }
   }
 }
 
 
 /**
- * Implementation of hook_nodeapi().
+ * Implementation of hook_form_alter() .
+ *
+ * Enable a 'peekability' radio selection in the workflow section of the node type configuration.
+ * Enable a 'peekable' checkbox in the node edit form for relevant node types as per peekability settings.
  */
 
+function peek_form_alter(&$form, &$form_state, $form_id) {
+  if ($form_id == 'node_type_form' && isset($form['identity']['type'])) {
+    $form['workflow']['peek'] = array(
+      '#type' => 'radios',
+      '#title' => t('Peeks'),
+      '#default_value' => variable_get('peek_'. $form['#node_type']->type, 1),
+      '#options' => array(
+        0 => t('Disabled'),
+        1 => t('Optional, default off for new nodes'),
+        2 => t('Optional, default on for new nodes'),
+      ),
+    );
+    return;
+  }
+
+  if (isset($form['type'])) {
+    if ($form['type']['#value'] .'_node_form' == $form_id) {
+      if ($type_peekability = variable_get('peek_'. $form['type']['#value'], 1)) {
+        $node = $form['#node'];
+        $form['options']['peekable'] = array(
+          '#type' => 'checkbox',
+          // Existing nodes use the current setting, new nodes depend on the
+          // per node type setting.
+          '#default_value' => empty($node->nid) ? (2 == $type_peekability ? 1 : 0) : $node->peekable,
+          '#access' => user_access('administer peeks'),
+          '#title' => t('Peekable'),
+          '#description' => t('Check this box to allow peeks to be generated for this node'),
+        );
+      }
+      else {
+        // Make sure we hard code a value on non-peekable nodes.
+        $form['options']['peekable'] = array(
+          '#type' => 'value',
+          '#value' => 0,
+        );
+      }
+    }
+    return;
+  }
+}
+
+/**
+ * Implementation of hook_nodeapi() .
+ */
 function peek_nodeapi(&$node, $op, $a3 = NULL, $a4 = NULL) {
   switch ($op) {
     case 'insert':
     case 'update':
-      _peek_peekability_save($node);
+      db_query("DELETE FROM {peekable} WHERE nid = %d", $node->nid);
+      if ($node->peekable) {
+        db_query("INSERT into {peekable} (nid) VALUES (%d)", $node->nid);
+      }
       break;
+
     case 'load':
       // load peekability and existing node peeks
-      $node->peekable = _peek_peekability($node->nid);
-      $node->nodepeeks = _peek_nodepeeks($node->nid);
-      break;
+      return array(
+        'peekable' => db_result(db_query('SELECT COUNT(nid) FROM {peekable} WHERE nid = %d', $node->nid)),
+        'nodepeeks' => db_result(db_query('SELECT COUNT(sid) FROM {peek} WHERE nid = %d', $node->nid)),
+      );
+
     case 'delete':
-      $sids = array();
-      $result = db_query('SELECT sid FROM {peek} p WHERE nid = %d',$node->nid);
-      while($p = db_fetch_array($result)) {
-        $sids[] = $p['sid'];
+      $result = db_query('SELECT sid FROM {peek} p WHERE nid = %d', $node->nid);
+      while ($p = db_fetch_array($result)) {
+        db_query("DELETE FROM {peek} WHERE sid = '%s'", $p->sid);
+        db_query("DELETE FROM {peek_access} WHERE sid = '%s'", $p->sid);
       }
-      if (count($sids)) {
-        db_query('DELETE FROM {peek} WHERE sid in ("'.implode('","',$sids).'")');
-        db_query('DELETE FROM {peek_access} WHERE sid in ("'.implode('","',$sids).'")');
-      }
-      db_query('DELETE FROM {peekable} WHERE nid = %d',$node->nid);
+      db_query('DELETE FROM {peekable} WHERE nid = %d', $node->nid);
       break;
   }
 }
 
 
 /**
- * Implementation of hook_perm().
+ * Implementation of hook_perm() .
  */
 function peek_perm() {
-  return array('generate peeks','administer peeks');
+  return array('generate peeks', 'administer peeks');
 }
 
 
@@ -211,294 +281,108 @@ function peek_perm() {
  */
 
 
-/**
- * Menu callback for the peek settings form.
- */
-function peek_admin_settings() {
-  $duration = (integer) variable_get('peek_duration',3600);
-  $expiry = (integer) variable_get('peek_expiry',3*24*3600); 
-  $form['settings_general'] = array(
-    '#type' => 'fieldset',
-    '#title' => t('Default settings'),
-    '#collapsible' => TRUE,
-  );
-  $form['settings_general']['peek_anonymous'] = array(
-    '#type' => 'checkbox',
-    '#title' => t('Anonymous Peeks'),
-    '#return_value' => 1,
-    '#default_value' => variable_get('peek_anonymous',0),
-    '#description' => t('If checked, don\'t generate new users when generating a peek for a non-user, use anonymous access.')
-  );
-  $form['settings_general']['peek_duration'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Peek duration'),
-    '#default_value' => $duration/3600,
-    '#size' => 15,
-    '#maxlength' => 10,
-    '#description' => t('Length of time in hours that the peek link remains valid after first access.'),
-    '#field_suffix' => '<kbd>'. t('hours') .'</kbd>'
-  );
-  $form['settings_general']['peek_expiry'] = array(
-    '#type' => 'textfield',
-    '#title' => t('Peek expiry'),
-    '#default_value' => $expiry/(24 * 3600),
-    '#size' => 15,
-    '#maxlength' => 10,
-    '#description' => t('Length of time in days that the first access of the peek link remains valid.'),
-    '#field_suffix' => '<kbd>'. t('days') .'</kbd>'
-  );
-  // don't do the normal settings submit, because I need to change units 
-  $form['#submit']['peek_admin_settings_submit'] = array();
-  return system_settings_form($form);
-}
 
 /**
- * Form API callbacks to validate and submit the peek settings form.
+ * Display a page with the user's peeks.
+ *
+ * @param $user object
  */
-
-function peek_admin_settings_validate($form_id, $form_values) {
-  if (!is_numeric($form_values['peek_duration'])) {
-    form_set_error('peek_duration', t('You must set the peek duration to a number.'));
-  }
-  if (!is_numeric($form_values['peek_expiry'])) {
-    form_set_error('peek_duration', t('You must set the peek expiry to a number.'));
-  }
-}
-
-function peek_admin_settings_submit($form_id, $form_values) {
-  $duration = (integer) (3600 * $form_values['peek_duration']);
-  if ($duration) {
-    variable_set('peek_duration',$duration);
-  }
-  $expiry = (integer) (24 * 3600 * $form_values['peek_expiry']);
-  if ($expiry) {
-    variable_set('peek_expiry',$expiry);
+function peek_user_page($user) {
+  $output = peek_table(array('uid' => $user->uid));
+  if (!$output) {
+    $output = t('No peeks');
   }
-  variable_set('peek_anonymous',$form_values['peek_anonymous']);
-  drupal_set_message(t('The default peek values have been updated.'));
+  return $output;
 }
 
 /**
- *
- * function peek_generate_form
- *
  * Display/validate/process a form to generate a peek session
- *
  */
-
-function peek_generate_form($nid) {
-  $form['peek_info'] = array('#type' => 'fieldset', '#title' => t('Existing peeks'));
-  $table = peek_table(array('nid' => $nid));
-  if ($table) {
-    $form['peek_info']['peeks'] = array('#type' => 'markup', '#value' => $table);
-  }
-  else {
-    $form['peek_info']['peeks'] = array('#type' => 'markup', '#value' => theme('placeholder',t('None')));
-  }
-  $form['peek_generate'] = array('#type' => 'fieldset', '#title' => t('New peek'));
-  $form['peek_generate']['nid'] = array('#type' => 'hidden', '#value' => $nid);
-  $form['peek_generate']['mail'] = array('#type' => 'textfield','#title' => t('To:'),'#required' => TRUE);
-  $form['peek_generate']['bccmail'] = array('#type' => 'textfield','#title' => t('Bcc:'));
-  $form['peek_generate']['notify'] = array('#type' => 'textfield','#title' => t('Notify:'));
-  $form['submit'] = array('#type' => 'submit', '#value' => t('Generate a peek'), '#weight' => 20);
+function peek_generate_form(&$form_state, $node) {
+  $form['peek_generate'] = array(
+    '#type' => 'fieldset',
+    '#title' => t('New peek'),
+    '#description' => t('Enter the email addresses that the Peek should be sent to. Separate multiple email addresses with commas.'),
+  );
+  $form['peek_generate']['node'] = array(
+    '#type' => 'value',
+    '#value' => $node,
+  );
+  $form['peek_generate']['mail'] = array(
+    '#type' => 'textfield',
+    '#title' => t('To'),
+    '#required' => TRUE,
+    '#description' => t('These email addresses will receive an an email with the Peek link. If the email address is not associated with a user account on this site a new account will be created for them.'),
+  );
+  $form['peek_generate']['bcc'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Bcc'),
+    '#description' => t('These email addresses will receive an BCCed copied of the email sent to the user.'),
+  );
+  $form['peek_generate']['notify'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Notify'),
+    '#description' => t('These email addresses will receive an email the first time the Peek is accessed.'),
+  );
+  $form['peek_generate']['submit'] = array('#type' => 'submit', '#value' => t('Generate a peek'), '#weight' => 20);
   return $form;
 }
 
-function peek_generate_form_validate($form_id,$form_values) {
+function peek_generate_form_validate($form, &$form_state) {
   // validate the email formats in each of the email fields
-  foreach(array('mail','bccmail','notify') as $key) {
-    if ($value = $form_values[$key]) {
-      $ma = explode(',',$value);
-      foreach($ma as $mail) {
-        if ($msg = user_validate_mail($mail)) {
-          form_set_error($key,t('Invalid %type: email address format (%msg)',array('%type' => $key, '%msg' => $msg)));
-          end(); // don't bother testing any more in this group
-        } 
+  foreach (array('mail', 'bcc', 'notify') as $key) {
+    if ($value = $form_state['values'][$key]) {
+      foreach (explode(', ' , $value) as $mail) {
+        if (!valid_email_address($mail)) {
+          form_set_error($key, t('The e-mail address %mail is not valid.', array('%mail' => $mail)));
+        }
       }
     }
   }
-  return $form_values;
 }
 
-function peek_generate_form_submit($form_id,$form_values) {
+function peek_generate_form_submit($form, &$form_state) {
   global $user;
-  $to = explode(',',$form_values['mail']);
-  $bcc = $form_values['bcc'];
-  $notify = $form_values['notify'];
-  $headers = $bcc ? array('bcc' => $bcc) : array();
-  $nid = $form_values['nid'];
-  $node = node_load($nid);
-  $subject = theme('peek_subject',$node);
-  $from = $user->mail ? $user->mail : variable_get('site_mail', ini_get('sendmail_from'));
-  foreach($to as $mail) { 
-    $path = peek_fetch($nid,$mail,$notify);
-    $body = theme('peek_body',$path,$node);
-    drupal_mail('peek-gen', $mail, $subject, $body, $from, $headers);
-  }
-  drupal_set_message(t('%count Peek(s) sent',array('%count' => count($to))));
-  watchdog('peek log',t('Peek(s) sent to %to',array('%to' => implode(',',$to))),WATCHDOG_NOTICE);
-  return array('node/'. $nid.'/peek');
-}
-
-/**
- * peek_show
- *
- * Show a peek of a node
- *
- */
+  $node = $form_state['values']['node'];
+  $to = explode(', ', $form_state['values']['mail']);
+  // Leave bcc and notify as strings since we don't need to convert them into
+  // users.
+  $bcc = $form_state['values']['bcc'];
+  $notify = $form_state['values']['notify'];
 
-function peek_show($uid,$timestamp,$sid) {
+  $from = $user->mail ? $user->mail : variable_get('site_mail', ini_get('sendmail_from'));
 
-  global $user;
+  $headers = $bcc ? array('bcc' => $bcc) : array();
+  $subject = theme('peek_subject', $node);
 
-  $current = time();
-  // simple url syntax security checks
-  if (!is_numeric($timestamp) || $timestamp > $current || !is_numeric($uid) || empty($sid)) {
-    watchdog('peek log',t('Invalid peek attempt: URL syntax'),WATCHDOG_WARNING);
-    return drupal_not_found();
-  }
-  // user security check
-  $account = user_load(array('uid' => $uid));
-  if (!$account->uid && !variable_get('peek_anonymous',0)) {
-    watchdog('peek log',t('Invalid peek attempt: invalid user'),WATCHDOG_WARNING);
-    return drupal_access_denied();
-  }
-  elseif($user->uid) {
-    if ($account->uid != $user->uid) {
-      watchdog('peek log',t('Invalid peek attempt: wrong user'),WATCHDOG_WARNING);
-      return drupal_access_denied();
-    }
-  }
-  elseif($account->uid)  { // switch to this user
-    $user = $account;
-    cache_clear_all($account->uid .':', 'cache_menu', TRUE);
-    drupal_goto($_GET['q']);
-  }
-  // peek security checks
-  $peek = db_fetch_object(db_query("SELECT * from {peek} WHERE sid = '%s' AND created = %d",$sid,$timestamp));
-  if ($sid != $peek->sid) {
-    watchdog('peek log',t('Invalid peek attempt: invalid sid or timestamp'),WATCHDOG_WARNING);
-    return drupal_not_found();
-  }
-  $invalid = _peek_status($peek,$current);
-  if (count($invalid)) {
-    watchdog('peek log',t('Invalid peek attempt: %invalid', array('%invalid' => implode(', ',$invalid))),WATCHDOG_WARNING);
-    return drupal_access_denied();
-  }
-  // end of security checks
-  if (!$peek->first_access) {
-    db_query("UPDATE {peek} SET first_access = %d WHERE sid = '%s'",$current, $sid);
-    watchdog('peek log',t('First peek for sid: %s',array('%s' => $sid)),WATCHDOG_WARNING);
-    if ($peek->notify) {
-      $notify = explode(',',$peek->notify);
-      $subject = t('Peek: access notification');
-      $body = theme('peek_notify',$peek);
-      $from = variable_get('site_mail', ini_get('sendmail_from'));
-      $to = array();
-      foreach($notify as $mail) {
-        $mail = trim($mail);
-        if ($msg = user_validate_mail($mail)) {
-          watchdog('peek log',$msg,WATCHDOG_WARNING);
-        }
-        else {
-          $to[] = $mail;
-        }
-      } 
-      if (count($to)) {     
-        drupal_mail('peek-notify', implode(',',$to), $subject, $body, $from);
-      }
-    }
-  }
-  db_query("INSERT into {peek_access} (sid,hostname,timestamp) VALUES ('%s','%s',%d)",$sid,$_SERVER['REMOTE_ADDR'],$current);
-  /* now switch to the node page if i've got view permissions, or otherwise just pretend i'm actually on the node page */
-  $nid = $peek->nid;
-  if ($user->status) {
-    $node = node_load($nid);
-    if (node_access('view',$node)) {
-      drupal_goto('node/'.$nid);
-      exit;
-    }
+  foreach ($to as $mail) {
+    $path = peek_fetch($node, $mail, $notify);
+    $body = theme('peek_body', $path, $node);
+    drupal_mail('peek-gen', $mail, $subject, $body, $from, $headers);
   }
-  $_GET['q'] = 'node/'.$nid;
-  $node = node_load($nid);
-  return node_page_view($node);
-}
-
 
-/**
- * peek_content_administer
- *
- * Display a list of generated peeks and their status
- *
- */
-
-function peek_content_administer($params = NULL,$title = 'Peek Administration') {
-
-  if (!is_array($params)) {
-    $params = array();
-  }
-  if ($title) {
-    drupal_set_title(t(check_plain($title)));
-  }
-  $output = peek_table($params);
-  if (!$output) {
-    $output = t('No peeks');
-  }
-  return $output;
-  
+  drupal_set_message(format_plural(count($to), '1 Peek sent', '%count Peeks sent', array('%count' => count($to))));
+  watchdog('peek log', 'Peek(s) sent to %to', array('%to' => implode(', ', $to)), WATCHDOG_NOTICE);
+  $form_state['redirect'] = array('node/'. $node->nid .'/peek');
 }
 
+
 /**
- * peek_peek
+ * Load a single peek object from the database.
  *
- * Display full detail about a peek access times and provide an opportunity to delete it.
+ * Used for the %peek menu wildcard handler.
  *
+ * @param $sid
  */
-
-function peek_peek_form() {
-  $sid = arg(4);
-  $breadcrumb = drupal_get_breadcrumb();
-  $breadcrumb[] = 'Detail';
-  drupal_set_breadcrumb($breadcrumb);
-  $peek = db_fetch_object(db_query('SELECT p.*,u.mail,n.title FROM {peek} p INNER JOIN {users} u ON p.uid = u.uid INNER JOIN {node} n ON p.nid = n.nid WHERE p.sid = "%s"', $sid));
-  $info = theme('peek_info',$peek);
-  $form['peek_info'] = array('#type' => 'fieldset', '#title' => t('Peek'));
-  $form['peek_info']['peek'] = array('#type' => 'markup', '#value' => $info);
-  $form['peek_info']['sid'] = array('#type' => 'hidden', '#name' => 'sid', '#value' => $peek->sid);
-  $form['peek_info']['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
-
-  $result = db_query('SELECT hostname,timestamp FROM {peek_access} WHERE sid = "%s"', $sid);
-  $rows = array();
-  while($p = db_fetch_array($result)) {
-    $p['timestamp'] = format_date($p['timestamp']);
-    $rows[] = $p;
-  }
-  if ($rows) {
-    $header = array('hostname','date/time');
-    $log = theme('table',$header,$rows);
-    $form['peek_log'] = array('#type' => 'fieldset', '#title' => t('Access log'));
-    $form['peek_log']['log'] = array('#type' => 'markup', '#value' => $log);
-  }
-  return $form;
-}
-
-function peek_peek_form_submit($form_id,$form_values) {
-  $sid = $form_values['sid'];
-  _peek_delete($sid);
-  drupal_set_message(t('Peek deleted.'));
-  return array('admin/content/peek');
+function peek_load($sid) {
+  return db_fetch_object(db_query('SELECT p.*, u.mail, n.title FROM {peek} p INNER JOIN {users} u ON p.uid = u.uid INNER JOIN {node} n ON p.nid = n.nid WHERE p.sid = "%s"', $sid));
 }
 
 /**
  * Internal functions
  */
 
-function _peek_delete($sid) {
-  db_query("DELETE FROM {peek} WHERE sid = '%s'",$sid);
-  db_query("DELETE FROM {peek_access} WHERE sid = '%s'",$sid);
-}
-
-function _peek_status($peek,$current = NULL) {
+function _peek_status($peek, $current = NULL) {
   $current = $current ? $current : time();
   $invalid =  array();
   if ( $peek->expires < $current ) {
@@ -510,48 +394,28 @@ function _peek_status($peek,$current = N
   return $invalid;
 }
 
-function _peek_nodepeeks($nid) {
-  return db_result(db_query('SELECT count(sid) from {peek} WHERE nid = %d',$nid));
-}
-
-function _peek_peekability($nid) {
-  return db_result(db_query('SELECT nid from {peekable} WHERE nid = %d',$nid));
-}
-
-function _peek_peekability_save($node) {
-  if ($node->set_peekable) {
-    if (!$node->peekable) {
-      db_query("INSERT into {peekable} (nid) VALUES (%d)",$node->nid);
-    }
-  }
-  else {
-    if ($node->peekable) {
-      db_query("DELETE FROM {peekable} WHERE nid = %d",$node->nid);
-    }
-  }
-}
 
 /* return a normal link or a text link for emailing */
 
-function _peek_link($title,$path,$local) {
-  return $local ? l($title,$path) : (check_plain($title).' '.url($path,NULL,NULL,TRUE));
+function _peek_link($title, $path, $local) {
+  return $local ? l($title, $path) : (check_plain($title) .' '. url($path, array('absolute' => TRUE)));
 }
 
 /* return an array of peek details, for local display or (remote/text) emailing */
 
-function _peek_info($p,$local = TRUE) {
-  $rows[] = array('Title',_peek_link($p->title,'node/'.$p->nid,$local));
-  $rows[] = array('User',_peek_link($p->mail,'user/'.$p->uid,$local));
-  $rows[] = array('Created',format_date($p->created));
-  $rows[] = array('Expires',format_date($p->expires));
-  $rows[] = array('Duration',$p->duration);
-  $rows[] = array('First Access',$p->first_access ? format_date($p->first_access) : theme('placeholder',t('None')));
-  $rows[] = array('Notification',$p->notify ? $p->notify : theme('placeholder',t('None')));
-  $status = implode(', ',_peek_status($p));
-  $status = $status ? t('Inactive: %status',array('%status' => $status)) : t('Active');
-  $rows[] = array('Status',$status);
+function _peek_info($p, $local = TRUE) {
+  $rows[] = array('Title', _peek_link($p->title, 'node/'. $p->nid, $local));
+  $rows[] = array('User', _peek_link($p->mail, 'user/'. $p->uid, $local));
+  $rows[] = array('Created', format_date($p->created));
+  $rows[] = array('Expires', format_date($p->expires));
+  $rows[] = array('Duration', format_interval($p->duration));
+  $rows[] = array('First Access', $p->first_access ? format_date($p->first_access) : theme('placeholder', t('None')));
+  $rows[] = array('Notification', $p->notify ? $p->notify : theme('placeholder', t('None')));
+  $status = implode(', ', _peek_status($p));
+  $status = $status ? t('Inactive: %status', array('%status' => $status)) : t('Active');
+  $rows[] = array('Status', $status);
   if (!$local) {
-    $rows[] = array('Details',url('admin/content/peek/peek/'.$p->sid,NULL,NULL,TRUE));
+    $rows[] = array('Details', url('admin/content/peek/edit/'. $p->sid, array('absolute' => TRUE)));
   }
   return $rows;
 }
@@ -563,130 +427,112 @@ function _peek_info($p,$local = TRUE) {
  * Utility function to display a list of peeks, filtered by params.
  * Used in most of the administration pages.
  *
+ * @param $params An array of values to filter the peek table by. Key is the
+ *   field name value is the value.
  */
+function peek_table($params = array(), $limit = 10) {
+  $args = array();
 
-function peek_table($params = NULL,$limit = 10) {
-
-  if (!is_array($params)) {
-    $params = array();
-  }
-
-  $select = array('uid' => '%d', 'nid' => '%d', 'first_access' => '%d', 'created' => '%d', 'expires' => '%d');
+  $displayed_fields = array(
+    'nid'           => array('data' => t('Node'), 'field' => 'nid'),
+    'uid'           => array('data' => t('User'), 'field' => 'uid'),
+    'first_access'  => array('data' => t('First accessed'), 'field' => 'first_access'),
+    'created'       => array('data' => t('Created'), 'field' => 'created', 'sort' => 'desc'),
+    'expires'       => array('data' => t('Expires'), 'field' => 'expires')
+  );
+  $filterable_fields = array('uid' => '%d', 'nid' => '%d', 'first_access' => '%d', 'created' => '%d', 'expires' => '%d');
   $where = array();
-  if (count($params)) {
-    foreach($params as $key => $value) {
-      if ($replace = $select[$key]) {
-        $where[] = "p.$key = $replace";
-        unset($select[$key]);
-      }
-      else {
-        unset($params[$key]);
-      }
+  foreach ($params as $field => $value) {
+    if (isset($filterable_fields[$field])) {
+      $where[] = "p.$field = ". $filterable_fields[$field];
+      $args[] = $value;
+      // If they're filtering by a value no need to display it since they'll
+      // all e identical.
+      unset($displayed_fields[$field]);
     }
   }
-  $keys = array();
-  foreach(array_keys($select) as $key) {
-    $keys[] = 'p.'.$key;
-  }
-  $join = '';
-  if ($select['uid']) {
-    $keys[] = 'u.mail';
-    $join .= ' INNER JOIN {users} u ON p.uid = u.uid';
-  }
-  if ($select['nid']) {
-    $keys[] = 'n.title';
-    $join .= ' INNER JOIN {node} n ON p.nid = n.nid';
-  }
-  $sql = 'SELECT sid,'.implode(',',$keys).' FROM {peek} p'.$join;
-  if (count($where)) {
-    $sql .= ' WHERE '.implode(' AND ',$where);
-  }
-  $sql .= ' ORDER BY p.created DESC';
-  $result = db_query($sql,$params);
-  if (!db_num_rows($result)) return FALSE;
-  $peeks = array();
-  $header = array();
-  foreach($select as $key => $replace) { 
-    switch($key) {
-      case 'uid' :
-        $header[] = 'user'; break;
-      case 'nid' :
-        $header[] = 'title'; break;
-      default :
-        $header[] = $key; break;
-    }
-  }
-  $anon = l(variable_get('anonymous', t('Anonymous')),'user/0/peek');
-  while($p = db_fetch_array($result)) {
-    $peek = array();
-    foreach($select as $key => $replace) { 
-      switch($key) {
+
+  $headers = array_merge(array_values($displayed_fields), array('data' => t('Operations')));
+
+  $sql = "SELECT p.*, n.title, u.mail FROM {peek} p INNER JOIN {node} n ON p.nid = n.nid INNER JOIN {users} u ON p.uid = u.uid";
+  if (!empty($where)) {
+    $sql .= ' WHERE '. implode(' AND ', $where);
+  }
+  $sql .= tablesort_sql($headers);
+
+  $destination = drupal_get_destination();
+  $anon = l(variable_get('anonymous', t('Anonymous')), 'user/0/peek');
+
+  $rows = array();
+  $result = db_query($sql, $params);
+  while ($p = db_fetch_array($result)) {
+    $row = array();
+    foreach ($displayed_fields as $key => $replace) {
+      switch ($key) {
         case 'uid' :
-          $peek[] = $p['uid'] ? l($p['mail'],'user/'.$p['uid']) : $anon;
+          $row[] = $p['uid'] ? l($p['mail'], 'user/'. $p['uid']) : $anon;
           break;
         case 'nid' :
-          $peek[] = l($p['title'],'node/'.$p['nid']);
+          $row[] = l($p['title'], 'node/'. $p['nid']);
           break;
-        case 'created' :
-          $peek[] = l(format_date($p[$key]),'admin/content/peek/peek/'.$p['sid']);
+        case 'created':
+        case 'expires':
+        case 'first_access':
+          $row[] = $p[$key] ? format_date($p[$key]) : t('Never');
           break;
-        case 'first_access' :
-          $peek[] = $p['first_access'] ? format_date($p['first_access']) : t('none');
+        case 'duration':
+          $row[] = format_interval($p[$key]);
           break;
         default :
-          $peek[] = format_date($p[$key]);
+          $row[] = $p[$key];
           break;
       }
     }
-    $peeks[] = $peek;
-  } 
-  return theme('table',$header,$peeks);
+    $row[] = l(t('edit'), 'admin/content/peek/edit/'. $p['sid'], array('query' => $destination));
+    $rows[] = $row;
+  }
+
+  if (count($rows) == 0) {
+    return FALSE;
+  }
+
+  return theme('table', $headers, $rows);
 }
 
 /**
  * Themeing default funtions
  */
 
-function theme_peek_generate($form) {
-  return drupal_render($form);
-}
-
 /* functions for themeing the peek email */
 
 function theme_peek_subject($node) {
-  return 'FW: '.$node->title;
+  return t('FW: @title', array('@title' => $node->title));
 }
 
-function theme_peek_body($path,$node) {
-  return 'Click here: '.url($path,NULL,NULL,TRUE);
+function theme_peek_body($path, $node) {
+  return t('Click here: !link', array('!link' => url($path, array('absolute' => TRUE))));
 }
 
-/* function to theme the body of the peek first access notification email */
-
-function theme_peek_notify($peek) {
-  $p = db_fetch_object(db_query('SELECT p.*,u.mail,n.title FROM {peek} p INNER JOIN {users} u ON p.uid = u.uid INNER JOIN {node} n ON p.nid = n.nid WHERE p.sid = "%s"', $peek->sid));
-  $info = _peek_info($p,FALSE);
+/**
+ * function to theme the body of the peek first access notification email.
+ */
+function theme_peek_notify_email($peek) {
+  $p = db_fetch_object(db_query('SELECT p.*, u.mail, n.title FROM {peek} p INNER JOIN {users} u ON p.uid = u.uid INNER JOIN {node} n ON p.nid = n.nid WHERE p.sid = "%s"', $peek->sid));
+  $info = _peek_info($p, FALSE);
   $text = t("Peek notification information\n");
-  foreach($info as $row) {
-    $text .= "\n".implode(' : ',$row);
+  foreach ($info as $row) {
+    $text .= "\n". implode(' : ', $row);
   }
   return $text;
 }
 
-/* display full details about a peek */
-
-function theme_peek_info($p) {
-  $header = array();
-  $rows = _peek_info($p);
-  return theme('table',$header,$rows);
-}
 
 /**
  * API Functions
  *
  * These functions are intended to be also callable from other modules
  *
- * peek_get_user will generate a uid from an email address 
+ * peek_get_user will generate a uid from an email address
  * peek_generate will generate a peek
  * peek_fetch is a higher level integration these two function, probably
  *   the most useful one to use with other modules. It also has a
@@ -694,8 +540,8 @@ function theme_peek_info($p) {
  *
  */
 
-/* 
- * function peek_generate($param) 
+/*
+ * function peek_generate($param)
  *
  * This function can be called from other modules.
  * A return value is an error.
@@ -708,42 +554,66 @@ function theme_peek_info($p) {
 
 function peek_generate(&$param) {
   global $user;
-  
-  $nid = $param['nid'];
-  $uid = $param['uid'];
-  $notify = $param['notify'];
-  
+
   $msg = array();
-  if (!$nid) $msg['nid'] = t('missing "nid"');
-  if (!$uid && !variable_get('peek_anonymous',0)) $msg['uid'] = t('missing "uid"');
+  if (empty($param['nid'])) {
+    $msg['nid'] = t('missing "nid"');
+  }
+  if (empty($param['uid']) && !variable_get('peek_anonymous', 0)) {
+    $msg['uid'] = t('missing "uid"');
+  }
   if (count($msg)) {
-    return t('Invalid call to peek_generate: %msg',array('%msg',implode(',',$msg)));
+    return t('Invalid call to peek_generate: %msg', array('%msg', implode(', ', $msg)));
   }
-  if (!$sid = $param['sid']) {
-    $sid = md5(time().$nid.$uid);
-    $param['sid'] = $sid;
-  }
-  if (db_result(db_query('SELECT sid FROM {peek} WHERE sid = "%s"',$sid))) {
-    return t('Unexpected error in peek_generate, sid already exists: %sid',array('%sid',$sid));
-  }
-  // okay, we've passed the basic checks - now generate a new peek
-  if (!$duration = $param['duration']) {
-    $duration = variable_get('peek_duration',3600);
-    $param['duration'] = $duration;
-  }
-  $created = time();
-  if (!$expiry = $param['expiry']) {
-    $expiry = (integer) variable_get('peek_expiry',3*24*3600);
-    $param['expiry'] = $expiry;
-  }
-  db_query("INSERT into {peek} (sid,nid,uid,created,expires,duration,notify) VALUES ('%s',%d,%d,%d,%d,%d,'%s')",$sid,$nid,$uid,$created,($expiry + $created),$duration,$notify);
-  watchdog('peek log',t('Peek generated for uid %uid to access %url',array('%uid' => $uid,'%url' => l('node '.$nid,'node/'.$nid))),WATCHDOG_NOTICE);
-  $param['path'] = 'peek/'.$uid.'/'.$created.'/'.$sid;
-  return;
+
+  if (empty($param['sid'])) {
+    $param['sid'] = md5(time() . $param['nid'] . $param['uid']);
+  }
+  if (db_result(db_query('SELECT sid FROM {peek} WHERE sid = "%s"', $param['sid']))) {
+    return t('Unexpected error in peek_generate, sid already exists: %sid', array('%sid', $param['sid']));
+  }
+
+  // okay, we've passed the basic checks - either generate a new peek
+  // or generate a normal link if the user has permissions already
+  if (empty($param['peek'])) {
+    $current_user = $user;
+    $access = FALSE;
+    $user = user_load(array('uid' => $param['uid']));
+    if ($user->status) {
+      $node = node_load($param['nid']);
+      $access = node_access('view', $node);
+    }
+    $user = $current_user;
+    if ($access) {
+      watchdog('peek log', 'No peek neccesary for uid %uid to view node %nid', array('%uid' => $param['uid'], '%nid' => $param['nid']));
+      $param['path'] = 'node/'. $param['nid'];
+      return;
+    }
+  }
+  if (empty($param['duration'])) {
+    $param['duration'] = (int) variable_get('peek_duration', 3600);
+  }
+  if (empty($param['expiry'])) {
+    $param['expiry'] = (int) variable_get('peek_expiry', 3 * 24 * 3600);
+  }
+
+  $time = time();
+  $peek = array(
+    'sid' => $param['sid'],
+    'nid' => $param['nid'],
+    'uid' => $param['uid'],
+    'created' => $time,
+    'expires' => $param['expiry'] + $time,
+    'duration' => $param['duration'],
+    'notify' => $param['notify'],
+    'path' => 'peek/'. $param['uid'] .'/'. $time .'/'. $param['sid'],
+  );
+  drupal_write_record('peek', $peek);
+  watchdog('peek log', 'Peek generated for uid %uid to access %url', array('%uid' => $peek['uid'], '%url' => l('node '. $peek['nid'] , 'node/'. $peek['nid'])));
 }
 
-/*
- * peek_get_user($mail) 
+/**
+ * peek_get_user($mail)
  *
  * Return a user object for $mail, generating a new user if required
  * This doesn't actually involve anything to do with peeks and
@@ -755,29 +625,28 @@ function peek_generate(&$param) {
  * numeric suffixes to get uniqueness if necessary.
  *
  */
-
-function peek_get_user($mail,$roles = NULL) {
+function peek_get_user($mail, $roles = NULL) {
   if ($msg = user_validate_mail($mail)) {
     die($msg);
   }
-  $account = user_load(array('mail' => $mail)); 
-  if (!$account->uid && !variable_get('peek_anonymous',0)) { 
-    $name = current(explode('@',$mail));
+  $account = user_load(array('mail' => $mail));
+  if (empty($account->uid) && !variable_get('peek_anonymous', 0)) {
+    $name = current(explode('@', $mail));
     if ($msg = user_validate_name($name)) {
-      die(t('Unexpected error for %mail: %msg',array('%mail' => $mail,'%msg' => $msg)));
+      die(t('Unexpected error for %mail: %msg', array('%mail' => $mail, '%msg' => $msg)));
     }
     $plus = ''; $i = 1;
     while ($i < 100) {
-      $uid = db_result(db_query("SELECT uid from {users} WHERE name = '%s'",$name.$plus));
+      $uid = db_result(db_query("SELECT uid from {users} WHERE name = '%s'", $name . $plus));
       if ($uid) {
-        $plus = '_'. $i++; 
+        $plus = '_'. $i++;
       }
       else {
         break;
       }
     }
     if (100 <= $i) {
-      die('Unable to generate user name from '.$name.' after 100 tries, giving up.');
+      die('Unable to generate user name from '. check_plain($name) .' after 100 tries, giving up.');
     }
     $name .= $plus;
     $pass = user_password();
@@ -787,35 +656,33 @@ function peek_get_user($mail,$roles = NU
   return $account;
 }
 
-/* 
- * function peek_fetch($nid,$email,$notify)
+/**
+ * Returns a link that $email can use to access node $nid
  *
- * Returns a link that $email can use to access node $nid. 
- * It will check for permissions of the current user before generating it, and 
+ * It will check for permissions of the current user before generating it, and
  * will use the peek url only if the email doesn't already have access to it.
  *
- * Optional third argument is a comma-separated string of email addresses
- * to notify on first access of the peek.
+ * @param $node Node to generate the peek for.
+ * @param $email User's email address
+ * @param $notify Optional, comma-separated string of email addresses to notify
+ *   on first access of the peek.
  *
  */
-
-function peek_fetch($nid,$email,$notify = '') {
-
-  $node = node_load($nid);
-  if (!node_access('view',$node)) {
+function peek_fetch($node, $email, $notify = '') {
+  if (!node_access('view', $node)) {
     drupal_set_message(t('Unable to generate a peek for this node, no view access'));
     return FALSE;
   }
 
   $account = peek_get_user($email);
   $uid = $account->uid ? $account->uid : 0;
-  $peek = array('nid' => $nid,'uid' => $uid);
-  if ($notify) {
+  $peek = array('nid' => $node->nid, 'uid' => $uid);
+  if (!empty($notify)) {
     $peek['notify'] = $notify;
   }
   $msg = peek_generate($peek);
   if ($msg) {
-    drupal_set_message(t('Unexpected error: %msg',array('%msg' => $msg)));
+    drupal_set_message(t('Unexpected error: %msg', array('%msg' => $msg)));
     return FALSE;
   }
   return $peek['path'];
Index: peek.pages.inc
===================================================================
RCS file: peek.pages.inc
diff -N peek.pages.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ peek.pages.inc	10 Sep 2008 01:48:12 -0000
@@ -0,0 +1,106 @@
+<?php
+// $Id$
+
+function peek_node_page($node) {
+  $output = '';
+
+  if ($node->nodepeeks) {
+    $table = peek_table(array('nid' => $node->nid));
+    $output .= empty($table) ? t('No peeks') : $table;
+  }
+  if ($node->peekable) {
+    $output .= drupal_get_form('peek_generate_form', $node);
+  }
+
+  return $output;
+}
+
+/**
+ * peek_show
+ *
+ * Show a peek of a node
+ *
+ */
+function peek_show($user, $timestamp, $peek) {
+  global $user;
+
+  $current = time();
+  // simple url syntax security checks
+  if (!is_numeric($timestamp) || $timestamp > $current || !is_numeric($uid) || empty($sid)) {
+    watchdog('peek log', 'Invalid peek attempt: URL syntax', array(), WATCHDOG_WARNING);
+    return drupal_not_found();
+  }
+  // user security check
+  $account = user_load(array('uid' => $uid));
+  if (!$account->uid && !variable_get('peek_anonymous', 0)) {
+    watchdog('peek log', 'Invalid peek attempt: invalid user', array(), WATCHDOG_WARNING);
+    return drupal_access_denied();
+  }
+  elseif ($user->uid) {
+    if ($account->uid != $user->uid) {
+      watchdog('peek log', 'Invalid peek attempt: wrong user', array(), WATCHDOG_WARNING);
+      return drupal_access_denied();
+    }
+  }
+  elseif ($account->uid)  { // switch to this user
+    $user = $account;
+    cache_clear_all($account->uid .':', 'cache_menu', TRUE);
+    drupal_goto($_GET['q']);
+  }
+  // peek security checks
+  $peek = db_fetch_object(db_query("SELECT * FROM {peek} WHERE sid = '%s' AND created = %d", $sid, $timestamp));
+  if ($timestamp != $peek->created) {
+    watchdog('peek log', 'Invalid peek attempt: invalid sid or timestamp', array(), WATCHDOG_WARNING);
+    return drupal_not_found();
+  }
+  $invalid = _peek_status($peek, $current);
+  if (count($invalid)) {
+    watchdog('peek log', 'Invalid peek attempt: %invalid', array('%invalid' => implode(', ', $invalid)), WATCHDOG_WARNING);
+    return drupal_access_denied();
+  }
+  // end of security checks
+  if (!$peek->first_access) {
+    db_query("UPDATE {peek} SET first_access = %d WHERE sid = '%s'", $current, $sid);
+    watchdog('peek log', 'First peek for sid: %s', array('%s' => $sid), WATCHDOG_WARNING);
+    if ($peek->notify) {
+      $notify = explode(', ', $peek->notify);
+      $subject = t('Peek: access notification');
+      $body = theme('peek_notify_email', $peek);
+      $from = variable_get('site_mail', ini_get('sendmail_from'));
+      $to = array();
+      foreach ($notify as $mail) {
+        $mail = trim($mail);
+        if ($msg = user_validate_mail($mail)) {
+// TODO: the $msg string shouldn't be used because it's been translated... we need something static here.
+          watchdog('peek log', $msg, array(), WATCHDOG_WARNING);
+        }
+        else {
+          $to[] = $mail;
+        }
+      }
+      if (count($to)) {
+        drupal_mail('peek-notify', implode(', ', $to), $subject, $body, $from);
+      }
+    }
+  }
+
+  $peek_access = array(
+    'sid' => $sid,
+    'hostname' => ip_address(),
+    'timestamp' => $current,
+  );
+  drupal_write_record('peek_access', $peek_access);
+
+  /* now switch to the node page if i've got view permissions, or otherwise just pretend i'm actually on the node page */
+  $nid = $peek->nid;
+  if ($user->status) {
+    $node = node_load($nid);
+    if (node_access('view', $node)) {
+      drupal_goto('node/'. $nid);
+      exit;
+    }
+  }
+  $_GET['q'] = 'node/'. $nid;
+  $node = node_load($nid);
+  return node_page_view($node);
+}
