diff -urpN drupal-6.x-dev-200708221235/includes/bootstrap.inc drupal-6.x-dev-access-0.3/includes/bootstrap.inc --- drupal-6.x-dev-200708221235/includes/bootstrap.inc 2007-08-07 16:41:24.000000000 +0800 +++ drupal-6.x-dev-access-0.3/includes/bootstrap.inc 2007-08-22 14:32:11.000000000 +0800 @@ -792,11 +792,55 @@ 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. + // NOTE: non-regexp matches are always done first. + $array = array(); + while ($rule = db_fetch_object($result)) { + // Phase is not starting with '^': in plain text format. Escape it + // with preg_quote(). + if (substr($rule->mask, 0, 1) != '^') { + $plain[$rule->type][$rule->status ? 'allow' : 'deny'][] = '^' . preg_quote($rule->mask) . '$'; + } + // Phase is starting with '^': in regex expression. Use directly without + // additional processing. + else { + $regex[$rule->type][$rule->status ? 'allow' : 'deny'][] = $rule->mask; + } + + // Record which type of rules are existing. + $array[$rule->type][$rule->status ? 'allow' : 'deny'] = TRUE; + } + + // Build regex expression for caching. + foreach ($array as $type => $tmp) { + foreach ($tmp as $status => $mask) { + $accesses[$type][$status] = '/' . implode('|', array_merge((array) $plain[$type][$status], (array) $regex[$type][$status])) . '/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 +848,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 +924,7 @@ function drupal_bootstrap($phase) { } function _drupal_bootstrap($phase) { - global $conf; + global $conf, $access; switch ($phase) { @@ -899,6 +945,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-200708221235/modules/user/user.module drupal-6.x-dev-access-0.3/modules/user/user.module --- drupal-6.x-dev-200708221235/modules/user/user.module 2007-08-19 16:08:45.000000000 +0800 +++ drupal-6.x-dev-access-0.3/modules/user/user.module 2007-08-22 14:13:56.000000000 +0800 @@ -2007,6 +2007,7 @@ function user_admin_access_add($mask = N 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'); + cache_clear_all('accesses', 'cache'); drupal_set_message(t('The access rule has been added.')); drupal_goto('admin/user/rules'); } @@ -2038,6 +2039,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']); + cache_clear_all('accesses', 'cache'); drupal_set_message(t('The access rule has been deleted.')); $form_state['redirect'] = 'admin/user/rules'; return; @@ -2053,6 +2055,7 @@ function user_admin_access_edit($aid = 0 } else { db_query("UPDATE {access} SET mask = '%s', type = '%s', status = '%s' WHERE aid = %d", $edit['mask'], $edit['type'], $edit['status'], $aid); + cache_clear_all('accesses', 'cache'); drupal_set_message(t('The access rule has been saved.')); drupal_goto('admin/user/rules'); } @@ -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);