diff -urpN drupal-6.x-dev-200708270329/includes/bootstrap.inc drupal-6.x-dev-access-0.5/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.5/includes/bootstrap.inc 2007-08-27 14:22:15.000000000 +0800 @@ -403,6 +403,148 @@ 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 not starting with '^': in plain text format. Escape it + // with preg_quote(). + if (substr($rule->mask, 0, 1) != '^') { + $plain[$rule->type][$rule->status][] = '^' . preg_quote($rule->mask) . '$'; + } + // Mask is starting with '^': in regex expression. Use directly without + // additional processing. + else { + $regex[$rule->type][$rule->status][] = $rule->mask; + } + + // Record which type of rules are existing. + $record[$rule->type][$rule->status] = TRUE; + } + + // Build regex expression for caching. + foreach ((array) $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; +} + +/** + * 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; + if (isset($access[$type][1]) && $access[$type][1]) { + if (preg_match($access[$type][1], $mask)) { + $allow = TRUE; + } + } + if (isset($access[$type][0]) && $access[$type][0]) { + if (preg_match($access[$type][0], $mask)) { + $allow = FALSE; + } + } + 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; + if (isset($access[$type][0]) && $access[$type][0]) { + if (preg_match($access[$type][0], $mask)) { + $allow = FALSE; + } + } + if (isset($access[$type][1]) && $access[$type][1]) { + if (preg_match($access[$type][1], $mask)) { + $allow = TRUE; + } + } + break; + } + + 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 +934,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 +986,7 @@ function drupal_bootstrap($phase) { } function _drupal_bootstrap($phase) { - global $conf; + global $access, $conf; switch ($phase) { @@ -899,8 +1007,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.5/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.5/modules/user/user.module 2007-08-27 13:14:23.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. Have you forgotten your password?', 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') .'.
_: '. 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); @@ -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)) {