diff -urpN drupal-6.x-dev-200708270329/includes/bootstrap.inc drupal-6.x-dev-access-0.6/includes/bootstrap.inc
--- drupal-6.x-dev-200708270329/includes/bootstrap.inc	2007-08-27 00:00:29.000000000 +0800
+++ drupal-6.x-dev-access-0.6/includes/bootstrap.inc	2007-08-28 12:10:05.000000000 +0800
@@ -403,6 +403,212 @@ function drupal_get_filename($type, $nam
 }
 
 /**
+ * Load the persistent access table.
+ *
+ * The access table is composed of values that have been saved in the table
+ * with access_set().
+ */
+function access_init() {
+  // NOTE: caching the access rules improves performance by 5% when serving cached pages.
+  $accesses = array();
+  if ($cached = cache_get('accesses', 'cache')) {
+    $accesses = $cached->data;
+  }
+  else {
+    $result = db_query('SELECT * FROM {access}');
+
+    // Classify, group and preprocess fetched access rules.
+    while ($rule = db_fetch_object($result)) {
+      // Mask is starting with '^': in regex expression. Use directly without
+      // additional processing.
+      if (substr($rule->mask, 0, 1) == '^') {
+        $array[$rule->type][$rule->status]['regex'][] = $rule->mask;
+      }
+      // Mask is in full IP address format. Use numeric handling.
+      else if (preg_match('/^\d+(\.\d+){3}$/', $rule->mask)) {
+        $array[$rule->type][$rule->status]['ip'][] = array('address' => ip2long($rule->mask));
+      }
+      // Mask is in partial IP address format. Use numeric handling.
+      else if (preg_match('/^(\d+)\.?(\d+)?\.?(\d+)?\.?(\d+)?$/', $rule->mask, $matches)) {
+        $network = array();
+        $netmask = array();
+        for ($i = 1; $i < count($matches); $i++) {
+          $network[] = $matches[$i];
+          $netmask[] = '255';
+        }
+        for ($i = count($matches); $i <= 4; $i++) {
+          $network[] = '0';
+          $netmask[] = '0';
+        }
+        $network = implode('.', $network);
+        $netmask = implode('.', $netmask);
+        $lmask = ip2long($netmask);
+        $pair['network'] = ip2long($network) & $lmask;
+        $pair['broadcast'] = $pair['network'] | $lmask ^ ip2long('255.255.255.255');
+        $array[$rule->type][$rule->status]['ip'][] = $pair;
+      }
+      // Mask is in network/netmask pair format. Use numeric handling.
+      else if (preg_match('/^\d+(\.\d+){3}\/\d+(\.\d+){3}$/', $rule->mask)) {
+        list($network, $netmask) = explode('/', $rule->mask);
+        $lmask = ip2long($netmask);
+        $pair['network'] = ip2long($network) & $lmask;
+        $pair['broadcast'] = $pair['network'] | $lmask ^ ip2long('255.255.255.255');
+        $array[$rule->type][$rule->status]['ip'][] = $pair;
+      }
+      // Mask is in network/nnn CIDR specification. Use numeric handling.
+      else if (preg_match('/^\d+(\.\d+){3}\/\d+$/', $rule->mask)) {
+        $cidr = array("0.0.0.0", "128.0.0.0", "192.0.0.0", "224.0.0.0",
+                      "240.0.0.0", "248.0.0.0", "252.0.0.0", "254.0.0.0",
+                      "255.0.0.0", "255.128.0.0", "255.192.0.0", "255.224.0.0",
+                      "255.240.0.0", "255.248.0.0", "255.252.0.0", "255.254.0.0",
+                      "255.255.0.0", "255.255.128.0", "255.255.192.0", "255.255.224.0",
+                      "255.255.240.0", "255.255.248.0", "255.255.252.0", "255.255.254.0",
+                      "255.255.255.0", "255.255.255.128", "255.255.255.192", "255.255.255.224",
+                      "255.255.255.240", "255.255.255.248", "255.255.255.252", "255.255.255.254",
+                      "255.255.255.255");
+        list($network, $netmask) = explode('/', $rule->mask);
+        $netmask = $cidr[$netmask];
+        $lmask = ip2long($netmask);
+        $pair['network'] = ip2long($network) & $lmask;
+        $pair['broadcast'] = $pair['network'] | $lmask ^ ip2long('255.255.255.255');
+        $array[$rule->type][$rule->status]['ip'][] = $pair;
+      }
+      // Mask is in plain text format. Escape it with preg_quote().
+      else {
+        $array[$rule->type][$rule->status]['plain'][] = preg_quote($rule->mask);
+      }
+    }
+
+    // Build rules for caching.
+    foreach ((array) $array as $type => $tmp) {
+      foreach ((array) $tmp as $status => $format) {
+        if ($format['plain']) {
+          $accesses[$type][$status]['plain'] = '/^(' . implode('|', $format['plain']) . ')$/Ds';
+        }
+        if ($format['regex']) {
+          $accesses[$type][$status]['regex'] = '/' . implode('|', $format['regex']) . '/Ds';
+        }
+        if ($format['ip']) {
+          $accesses[$type][$status]['ip'] = $format['ip'];
+        }
+      }
+    }
+
+    cache_set('accesses', $accesses);
+  }
+
+  return $accesses;
+}
+
+/**
+ * Return a persistent access rule checking result.
+ *
+ * @param $type
+ *   Type of access to check.
+ * @param $mask
+ *   String or mask to test.
+ * @param $order
+ *   Controls the default access state and the order in which Allow and Deny
+ *   are evaluated. Handle in Apache mod_authz_host style.
+ * @return
+ *   TRUE if access is allowed, FALSE if access is denied.
+ */
+function access_get($type, $mask, $order = 'deny,allow') {
+  global $access;
+
+  switch(strtolower($order)) {
+    case 'allow,deny':
+    case 'mutual-failure':
+      // First, all Allow directives are evaluated; at least one must match,
+      // or the request is rejected. Next, all Deny directives are evaluated.
+      // If any matches, the request is rejected. Last, any requests which do
+      // not match an Allow or a Deny directive are denied by default.
+      $allow = FALSE;
+      $status = array(1, 0);
+      break;
+    case 'deny,allow':
+    default:
+      // First, all Deny directives are evaluated; if any match, the request
+      // is denied unless it also matches an Allow directive. Any requests
+      // which do not match any Allow or Deny directives are permitted.
+      $allow = TRUE;
+      $status = array(0, 1);
+      break;
+  }
+
+  while (!is_null($current_status = array_shift($status))) {
+    // Check with ip notation.
+    if ($mask == long2ip(ip2long($mask)) && isset($access[$type][$current_status]['ip']) && is_array($access[$type][$current_status]['ip'])) {
+      $ips = $access[$type][$current_status]['ip'];
+      foreach ($ips as $ip) {
+        if (isset($ip['address']) && $ip['address']) {
+          if ($ip['address'] == ip2long($mask)) {
+            $allow = $current_status ? TRUE : FALSE;
+          }
+        }
+        else {
+          if (ip2long($mask) >= $ip['network']  && ip2long($mask) <= $ip['broadcast']) {
+            $allow = $current_status ? TRUE : FALSE;
+          }
+        }
+      }
+    }
+    // Check with plain text notation.
+    if (isset($access[$type][$current_status]['plain']) && $access[$type][$current_status]['plain']) {
+      if (preg_match($access[$type][$current_status]['plain'], $mask)) {
+        $allow = $current_status ? TRUE : FALSE;
+      }
+    }
+    // Check with regex expression.
+    if (isset($access[$type][$current_status]['regex']) && $access[$type][$current_status]['regex']) {
+      if (preg_match($access[$type][$current_status]['regex'], $mask)) {
+        $allow = $current_status ? TRUE : FALSE;
+      }
+    }
+  }
+
+  return $allow;
+}
+
+/**
+ * Set a persistent access rule.
+ *
+ * @param $type
+ *   Type of access to set.
+ * @param $mask
+ *   String or mask to set.
+ * @param $status
+ *   Status of access to set.
+ * @param $aid
+ *   Access rule ID to alter.
+ */
+function access_set($type, $mask, $status, $aid = 0) {
+  if ($aid) {
+    db_query("UPDATE {access} SET mask = '%s', type = '%s', status = '%s' WHERE aid = %d", $mask, $type, $status, $aid);
+    if (!db_affected_rows()) {
+      $aid = 0;
+    }
+  }
+
+  if ($aid == 0) {
+    db_query("INSERT INTO {access} (mask, type, status) VALUES ('%s', '%s', %d)", $mask, $type, $status);
+  }
+
+  cache_clear_all('accesses', 'cache');
+}
+
+/**
+ * Unset a persistent access rule.
+ *
+ * @param $aid
+ *   Access rule ID to alter.
+ */
+function access_del($aid) {
+  db_query('DELETE FROM {access} WHERE aid = %d', $aid);
+  cache_clear_all('accesses', 'cache');
+}
+
+/**
  * Load the persistent variable table.
  *
  * The variable table is composed of values that have been saved in the table
@@ -792,40 +998,6 @@ function drupal_get_messages($type = NUL
 }
 
 /**
- * Perform an access check for a given mask and rule type. Rules are usually
- * created via admin/user/rules page.
- *
- * If any allow rule matches, access is allowed. Otherwise, if any deny rule
- * matches, access is denied.  If no rule matches, access is allowed.
- *
- * @param $type string
- *   Type of access to check: Allowed values are:
- *     - 'host': host name or IP address
- *     - 'mail': e-mail address
- *     - 'user': username
- * @param $mask string
- *   String or mask to test: '_' matches any character, '%' matches any
- *   number of characters.
- * @return bool
- *   TRUE if access is denied, FALSE if access is allowed.
- */
-function drupal_is_denied($type, $mask) {
-  // Because this function is called for every page request, both cached
-  // and non-cached pages, we tried to optimize it as much as possible.
-  // We deny access if the only matching records in the {access} table have
-  // status 0. If any have status 1, or if there are no matching records,
-  // we allow access. So, select matching records in decreasing order of
-  // 'status', returning NOT(status) for the first. If any have status 1,
-  // they come first, and we return NOT(status) = 0 (allowed). Otherwise,
-  // if we have some with status 0, we return 1 (denied). If no matching
-  // records, we get no return from db_result, so we return (bool)NULL = 0
-  // (allowed).
-  // The use of ORDER BY / LIMIT is more efficient than "MAX(status) = 0"
-  // in PostgreSQL <= 8.0.
-  return (bool) db_result(db_query_range("SELECT CASE WHEN status=1 THEN 0 ELSE 1 END FROM {access} WHERE type = '%s' AND LOWER(mask) LIKE LOWER('%s') ORDER BY status DESC", $type, $mask, 0, 1));
-}
-
-/**
  * Generates a default anonymous $user object.
  *
  * @return Object - the user object.
@@ -878,7 +1050,7 @@ function drupal_bootstrap($phase) {
 }
 
 function _drupal_bootstrap($phase) {
-  global $conf;
+  global $access, $conf;
 
   switch ($phase) {
 
@@ -899,8 +1071,11 @@ function _drupal_bootstrap($phase) {
       break;
 
     case DRUPAL_BOOTSTRAP_ACCESS:
+      // Initialize the access strategy.
+      $access = access_init();
+
       // Deny access to hosts which were banned - t() is not yet available.
-      if (drupal_is_denied('host', ip_address())) {
+      if (!access_get('host', ip_address())) {
         header('HTTP/1.1 403 Forbidden');
         print 'Sorry, '. check_plain(ip_address()) .' has been banned.';
         exit();
diff -urpN drupal-6.x-dev-200708270329/modules/user/user.module drupal-6.x-dev-access-0.6/modules/user/user.module
--- drupal-6.x-dev-200708270329/modules/user/user.module	2007-08-26 16:00:49.000000000 +0800
+++ drupal-6.x-dev-access-0.6/modules/user/user.module	2007-08-28 12:07:33.000000000 +0800
@@ -1194,7 +1194,7 @@ function user_login_name_validate($form,
       // blocked in user administration
       form_set_error('name', t('The username %name has not been activated or is blocked.', array('%name' => $form_state['values']['name'])));
     }
-    else if (drupal_is_denied('user', $form_state['values']['name'])) {
+    else if (!access_get('user', $form_state['values']['name'])) {
       // denied by access controls
       form_set_error('name', t('The name %name is a reserved username.', array('%name' => $form_state['values']['name'])));
     }
@@ -1663,7 +1663,7 @@ function _user_edit_validate($uid, &$edi
     else if (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE uid != %d AND LOWER(name) = LOWER('%s')", $uid, $edit['name'])) > 0) {
       form_set_error('name', t('The name %name is already taken.', array('%name' => $edit['name'])));
     }
-    else if (drupal_is_denied('user', $edit['name'])) {
+    else if (!access_get('user', $edit['name'])) {
       form_set_error('name', t('The name %name has been denied access.', array('%name' => $edit['name'])));
     }
   }
@@ -1675,7 +1675,7 @@ function _user_edit_validate($uid, &$edi
   else if (db_result(db_query("SELECT COUNT(*) FROM {users} WHERE uid != %d AND LOWER(mail) = LOWER('%s')", $uid, $edit['mail'])) > 0) {
     form_set_error('mail', t('The e-mail address %email is already registered. <a href="@password">Have you forgotten your password?</a>', array('%email' => $edit['mail'], '@password' => url('user/password'))));
   }
-  else if (drupal_is_denied('mail', $edit['mail'])) {
+  else if (!access_get('mail', $edit['mail'])) {
     form_set_error('mail', t('The e-mail address %email has been denied access.', array('%email' => $edit['mail'])));
   }
 }
@@ -1963,7 +1963,7 @@ function user_admin_access_check_validat
 function user_admin_access_check_submit($form, &$form_state) {
   switch ($form_state['values']['type']) {
     case 'user':
-      if (drupal_is_denied('user', $form_state['values']['test'])) {
+      if (!access_get('user', $form_state['values']['test'])) {
         drupal_set_message(t('The username %name is not allowed.', array('%name' => $form_state['values']['test'])));
       }
       else {
@@ -1971,7 +1971,7 @@ function user_admin_access_check_submit(
       }
       break;
     case 'mail':
-      if (drupal_is_denied('mail', $form_state['values']['test'])) {
+      if (!access_get('mail', $form_state['values']['test'])) {
         drupal_set_message(t('The e-mail address %mail is not allowed.', array('%mail' => $form_state['values']['test'])));
       }
       else {
@@ -1979,7 +1979,7 @@ function user_admin_access_check_submit(
       }
       break;
     case 'host':
-      if (drupal_is_denied('host', $form_state['values']['test'])) {
+      if (!access_get('host', $form_state['values']['test'])) {
         drupal_set_message(t('The hostname %host is not allowed.', array('%host' => $form_state['values']['test'])));
       }
       else {
@@ -2000,8 +2000,7 @@ function user_admin_access_add($mask = N
       form_set_error('mask', t('You must enter a mask.'));
     }
     else {
-      db_query("INSERT INTO {access} (aid, mask, type, status) VALUES ('%s', '%s', '%s', %d)", $aid, $edit['mask'], $edit['type'], $edit['status']);
-      $aid = db_last_insert_id('access', 'aid');
+      access_set($edit['type'], $edit['mask'], $edit['status']);
       drupal_set_message(t('The access rule has been added.'));
       drupal_goto('admin/user/rules');
     }
@@ -2032,7 +2031,7 @@ function user_admin_access_delete_confir
 }
 
 function user_admin_access_delete_confirm_submit($form, &$form_state) {
-  db_query('DELETE FROM {access} WHERE aid = %d', $form_state['values']['aid']);
+  access_del($form_state['values']['aid']);
   drupal_set_message(t('The access rule has been deleted.'));
   $form_state['redirect'] = 'admin/user/rules';
   return;
@@ -2047,7 +2046,7 @@ function user_admin_access_edit($aid = 0
       form_set_error('mask', t('You must enter a mask.'));
     }
     else {
-      db_query("UPDATE {access} SET mask = '%s', type = '%s', status = '%s' WHERE aid = %d", $edit['mask'], $edit['type'], $edit['status'], $aid);
+      access_set($edit['type'], $edit['mask'], $edit['status'], $aid);
       drupal_set_message(t('The access rule has been saved.'));
       drupal_goto('admin/user/rules');
     }
@@ -2078,7 +2077,7 @@ function user_admin_access_form(&$form_s
     '#size' => 30,
     '#maxlength' => 64,
     '#default_value' => $edit['mask'],
-    '#description' => '%: '. t('Matches any number of characters, even zero characters') .'.<br />_: '. t('Matches exactly one character.'),
+    '#description' => t('Start the line with a ^ character to designate a regular expression match.') . '<br />' . t('Supported IP address formats: full, partial, network/netmask pair, and network/nnn CIDR specification.') . '<br />' . t('NOTE: specific partial domain-name in regular expression match.'),
     '#required' => TRUE,
   );
   $form['submit'] = array('#type' => 'submit', '#value' => $submit);
@@ -2091,7 +2090,7 @@ function user_admin_access_form(&$form_s
  */
 function user_admin_access() {
   $header = array(array('data' => t('Access type'), 'field' => 'status'), array('data' => t('Rule type'), 'field' => 'type'), array('data' => t('Mask'), 'field' => 'mask'), array('data' => t('Operations'), 'colspan' => 2));
-  $result = db_query("SELECT aid, type, status, mask FROM {access}". tablesort_sql($header));
+  $result = db_query("SELECT aid, type, status, mask FROM {access} WHERE type = '%s' OR type = '%s' OR type = '%s'". tablesort_sql($header), 'host', 'mail', 'user');
   $access_types = array('user' => t('username'), 'mail' => t('e-mail'), 'host' => t('host'));
   $rows = array();
   while ($rule = db_fetch_object($result)) {
