diff -urpN drupal-6.x-dev-200708201800/includes/bootstrap.inc drupal-6.x-dev-access-0.2/includes/bootstrap.inc --- drupal-6.x-dev-200708201800/includes/bootstrap.inc 2007-08-07 16:41:24.000000000 +0800 +++ drupal-6.x-dev-access-0.2/includes/bootstrap.inc 2007-08-22 01:12:50.000000000 +0800 @@ -792,11 +792,51 @@ function drupal_get_messages($type = NUL } /** + * Initialize the access strategy. + */ +function _drupal_access_init() { + // NOTE: caching the access rules can greatly improve performance. + $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. + $array = array(); + while ($rule = db_fetch_object($result)) { + // Phase is not starting with '^': a plain text format. Escape it + // with preg_quote(). + if (substr($rule->mask, 0, 1) != '^') { + $array[$rule->type][$rule->status ? 'allow' : 'deny'][] = '^' . preg_quote($rule->mask) . '$'; + } + // Phase is starting with '^': a regex expression. No further more + // processing. + else { + $array[$rule->type][$rule->status ? 'allow' : 'deny'][] = $rule->mask; + } + } + + // Build regex expression for caching. + foreach ($array as $type => $tmp) { + foreach ($tmp as $status => $mask) { + $accesses[$type][$status] = '/' . implode('|', $mask) . '/Ds'; + } + } + + cache_set('accesses', $accesses); + } + + return $accesses; +} + +/** * 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. + * If any deny rule matches, access is denied. By the way, if any allow rule + * matches, access is allowed. If no rule matches, access is allowed. * * @param $type string * Type of access to check: Allowed values are: @@ -804,25 +844,27 @@ function drupal_get_messages($type = NUL * - 'mail': e-mail address * - 'user': username * @param $mask string - * String or mask to test: '_' matches any character, '%' matches any - * number of characters. + * String to test. * @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)); + global $access; + + // First of all, assume access is allowed. + $ret = FALSE; + + // Check if deny rules exists. If so, deny access if rule matches. + if (isset($access[$type]['deny']) && $access[$type]['deny'] && preg_match($access[$type]['deny'], $mask)) { + $ret = TRUE; + } + + // Check if allow rules exists. If so, allow access if rule matches. + if (isset($access[$type]['allow']) && $access[$type]['allow'] && preg_match($access[$type]['allow'], $mask)) { + $ret = FALSE; + } + + return $ret; } /** @@ -878,7 +920,7 @@ function drupal_bootstrap($phase) { } function _drupal_bootstrap($phase) { - global $conf; + global $conf, $access; switch ($phase) { @@ -899,6 +941,9 @@ function _drupal_bootstrap($phase) { break; case DRUPAL_BOOTSTRAP_ACCESS: + // Initialize the access strategy. + $access = _drupal_access_init(); + // Deny access to hosts which were banned - t() is not yet available. if (drupal_is_denied('host', ip_address())) { header('HTTP/1.1 403 Forbidden'); diff -urpN drupal-6.x-dev-200708201800/modules/user/user.module drupal-6.x-dev-access-0.2/modules/user/user.module --- drupal-6.x-dev-200708201800/modules/user/user.module 2007-08-19 16:08:45.000000000 +0800 +++ drupal-6.x-dev-access-0.2/modules/user/user.module 2007-08-22 01:14:25.000000000 +0800 @@ -2015,6 +2015,7 @@ function user_admin_access_add($mask = N $edit['mask'] = $mask; $edit['type'] = $type; } + cache_clear_all('accesses', 'cache'); return drupal_get_form('user_admin_access_add_form', $edit, t('Add rule')); } @@ -2040,6 +2041,7 @@ function user_admin_access_delete_confir db_query('DELETE FROM {access} WHERE aid = %d', $form_state['values']['aid']); drupal_set_message(t('The access rule has been deleted.')); $form_state['redirect'] = 'admin/user/rules'; + cache_clear_all('accesses', 'cache'); return; } @@ -2060,6 +2062,7 @@ function user_admin_access_edit($aid = 0 else { $edit = db_fetch_array(db_query('SELECT aid, type, status, mask FROM {access} WHERE aid = %d', $aid)); } + cache_clear_all('accesses', 'cache'); return drupal_get_form('user_admin_access_edit_form', $edit, t('Save rule')); } @@ -2083,7 +2086,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') .'.
_: '. t('Matches exactly one character.'), + '#description' => t('Start the line with a ^ character to designate a regular expression match.'), '#required' => TRUE, ); $form['submit'] = array('#type' => 'submit', '#value' => $submit);