diff -urpN drupal-6.x-dev-200708231745/includes/bootstrap.inc drupal-6.x-dev-access-0.4.2/includes/bootstrap.inc --- drupal-6.x-dev-200708231745/includes/bootstrap.inc 2007-08-07 16:41:24.000000000 +0800 +++ drupal-6.x-dev-access-0.4.2/includes/bootstrap.inc 2007-08-23 18:14:31.000000000 +0800 @@ -792,11 +792,60 @@ function drupal_get_messages($type = NUL } /** + * Initialize the access strategy. + */ +function _drupal_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. + $array = array(); + while ($rule = db_fetch_object($result)) { + $type = $rule->type; + $status = $rule->status ? 'allow' : 'deny'; + $mask = $rule->mask; + + // Mask is not starting with '^': in plain text format. Escape it + // with preg_quote(). + if (substr($mask, 0, 1) != '^') { + $plain[$type][$status][] = '^' . preg_quote($mask) . '$'; + } + // Mask is starting with '^': in regex expression. Use directly without + // additional processing. + else { + $regex[$type][$status][] = $mask; + } + + // Record which type of rules are existing. + $record[$type][$status] = TRUE; + } + + // Build regex expression for caching. + foreach ($record as $type => $tmp) { + foreach ($tmp as $status => $mask) { + // NOTE: non-regexp matches are always done first. + $rules = array_merge((array) $plain[$type][$status], (array) $regex[$type][$status]); + $accesses[$type][$status] = '/' . implode('|', $rules) . '/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 +853,33 @@ 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. + $denied = FALSE; + + // Check if deny rules exists. + if (isset($access[$type]['deny']) && $access[$type]['deny']) { + // If any deny rule matches, access is denied. + if (preg_match($access[$type]['deny'], $mask)) { + $denied = TRUE; + } + } + + // Check if allow rules exists. + if (isset($access[$type]['allow']) && $access[$type]['allow']) { + // If any allow rule matches, access is allowed. + if (preg_match($access[$type]['allow'], $mask)) { + $denied = FALSE; + } + } + + return $denied; } /** @@ -878,7 +935,7 @@ function drupal_bootstrap($phase) { } function _drupal_bootstrap($phase) { - global $conf; + global $conf, $access; switch ($phase) { @@ -899,6 +956,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-200708231745/modules/user/user.module drupal-6.x-dev-access-0.4.2/modules/user/user.module --- drupal-6.x-dev-200708231745/modules/user/user.module 2007-08-19 16:08:45.000000000 +0800 +++ drupal-6.x-dev-access-0.4.2/modules/user/user.module 2007-08-23 23:43:02.000000000 +0800 @@ -2005,8 +2005,8 @@ 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'); + db_query("INSERT INTO {access} (mask, type, status) VALUES ('%s', '%s', %d)", $edit['mask'], $edit['type'], $edit['status']); + cache_clear_all('accesses', 'cache'); drupal_set_message(t('The access rule has been added.')); drupal_goto('admin/user/rules'); } @@ -2038,6 +2038,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 +2054,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 +2085,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.') . '
' . t('NOTE: non-regexp matches are always done first.'), '#required' => TRUE, ); $form['submit'] = array('#type' => 'submit', '#value' => $submit);