Index: contributions/modules/cck_redirection/cck_redirection.module
===================================================================
RCS file: /cvs/drupal-contrib/contributions/modules/cck_redirection/cck_redirection.module,v
retrieving revision 1.2
diff -u -p -r1.2 cck_redirection.module
--- contributions/modules/cck_redirection/cck_redirection.module	25 Jun 2009 15:16:57 -0000	1.2
+++ contributions/modules/cck_redirection/cck_redirection.module	26 Jun 2009 05:26:48 -0000
@@ -1,5 +1,5 @@
 <?php
-// $Id: cck_redirection.module,v 1.2 2009/06/25 15:16:57 robinmonks Exp $
+// $Id$
 
 /**
  * @file
@@ -14,6 +14,10 @@ define('CCK_REDIRECTION_DIVERT', 0x000);
 define('CCK_REDIRECTION_DELAY', 0x001);
 define('CCK_REDIRECTION_FRAMESET', 0x002);
 define('CCK_REDIRECTION_NONE', 0x003);
+define('CCK_REDIRECTION_DOMAINS', 'aero|arpa|asia|biz|com|cat|coop|edu|gov|info|int|jobs|mil|museum|name|nato|net|org|pro|travel|mobi|local');
+define('CCK_REDIRECTION_FRONT', 'front');
+define('CCK_REDIRECTION_EXTERNAL', 'external');
+define('CCK_REDIRECTION_INTERNAL', 'internal');
 
 /*********************************************************************
  * Drupal Hooks
@@ -187,7 +191,20 @@ function cck_redirection_field_settings(
 function cck_redirection_field($op, &$node, $field, &$items, $teaser, $page) {
   switch ($op) {
     case 'validate':
-      // TODO: Do URI validation here.
+      foreach ($items as $delta => $item) {
+        if ($item['value']) {
+          if (cck_redirection_validate_url(trim($item['value'])) == FALSE) {
+            form_set_error($field['field_name'] .']['. $delta .'][value', t('Not a valid URL.'));
+          }
+        }
+      }
+      break;
+    case ('presave'):
+      foreach ($items as $delta => $item) {
+        if ($item['value']) {
+          $items[$delta]['value'] = cck_redirection_cleanup_url($items[$delta]['value']);
+        }
+      }
       break;
   } 
 }
@@ -618,3 +635,77 @@ function _cck_redirection_frameset($elem
     drupal_goto('redirect', 'uri=' . drupal_urlencode($element['#item']['value']));
   }
 }
+
+/**
+ * URL cleanup function "stolen" from CCK Link.
+ * Forms a valid URL if possible from an entered address.
+ * Trims whitespace and automatically adds an http:// to addresses without a protocol specified
+ *
+ * @param string $url
+ * @param string $protocol The protocol to be prepended to the url if one is not specified
+ */
+function cck_redirection_cleanup_url($url, $protocol = "http") {
+  $url = trim($url);
+  $type = cck_redirection_validate_url($url);
+
+  if ($type == CCK_REDIRECTION_EXTERNAL) {
+    // Check if there is no protocol specified.
+    $protocol_match = preg_match("/^([a-z0-9][a-z0-9\.\-_]*:\/\/)/i", $url);
+    if (empty($protocol_match)) {
+      // But should there be? Add an automatic http:// if it starts with a domain name.
+      $domain_match = preg_match('/^(([a-z0-9]([a-z0-9\-_]*\.)+)('. CCK_REDIRECTION_DOMAINS .'|[a-z]{2}))/i', $url);
+      if (!empty($domain_match)) {
+        $url = $protocol ."://". $url;
+      }
+    }
+  }
+
+  return $url;
+}
+
+/**
+ * URI validation function "stolen" and adapted from CCK Link.
+ * A lenient verification for URLs. Accepts all URLs following RFC 1738 standard
+ * for URL formation.
+ *
+ * @param string $text
+ * @return mixed Returns boolean FALSE if the URL is not valid. On success, returns an object with
+ * the following attributes: protocol, hostname, ip, and port.
+ */
+function cck_redirection_validate_url($text) {
+
+  $allowed_protocols = array('http', 'https');
+
+  $protocol = '(('. implode("|", $allowed_protocols) .'):\/\/)';
+  $authentication = '([a-z0-9]+(:[a-z0-9]+)?@)';
+  $domain = '(([a-z0-9]([a-z0-9\-_\[\]])*)(\.(([a-z0-9\-_\[\]])+\.)*('. CCK_REDIRECTION_DOMAINS .'|[a-z]{2}))?)';
+  $ipv4 = '([0-9]{1,3}(\.[0-9]{1,3}){3})';
+  $ipv6 = '([0-9a-fA-F]{1,4}(\:[0-9a-fA-F]{1,4}){7})';
+  $port = '(:([0-9]{1,5}))';
+
+  // Pattern specific to external links.
+  $external_pattern = '/^'. $protocol .'?'. $authentication .'?('. $domain .'|'. $ipv4 .'|'. $ipv6 .' |localhost)'. $port .'?';
+
+  // Pattern specific to internal links.
+  $internal_pattern = "/^([a-z0-9_\-+\[\]]+)";
+
+  $directories = "(\/[a-z0-9_\-\.~+%=&,$'!():;*@\[\]]*)*";
+  // Yes, four backslashes == a single backslash.
+  $query = "(\/?\?([?a-z0-9+_|\-\.\/\\\\%=&,$'():;*@\[\]]*))";
+  $anchor = "(#[a-z0-9_\-\.~+%=&,$'():;*@\[\]]*)";
+
+  // The rest of the path for a standard URL.
+  $end = $directories .'?'. $query .'?'. $anchor .'?'.'$/i';
+
+  if (strpos($text, '<front>') === 0) {
+    return CCK_REDIRECTION_FRONT;
+  }
+  if (preg_match($internal_pattern . $end, $text)) {
+    return CCK_REDIRECTION_INTERNAL;
+  }
+  if (preg_match($external_pattern . $end, $text)) {
+    return CCK_REDIRECTION_EXTERNAL;
+  }
+
+  return FALSE;
+}
