Commit b4ecbda9 authored by Neil Drumm's avatar Neil Drumm
Browse files

Drupal 5.2

parent 7933c050
Showing with 182 additions and 83 deletions
+182 -83
// $Id$
Drupal 5.2, xxxx-xx-xx
Drupal 5.2, 2007-07-26
----------------------
- changed hook_link() $teaser argument to match documentation.
- fixed a variety of small bugs.
- fixed a security issue (cross-site request forgery), see SA-2007-017
- fixed a security issue (cross-site scripting), see SA-2007-018
Drupal 5.1, 2007-01-29
----------------------
......@@ -87,6 +89,10 @@ Drupal 5.0, 2007-01-15
* added nested lists generation.
* added a self-clearing block class.
Drupal 4.7.7, 2007-07-26
------------------------
- fixed security issue (XSS), see SA-2007-018
Drupal 4.7.6, 2007-01-29
------------------------
- fixed security issue (code execution), see SA-2007-005
......
......@@ -200,7 +200,7 @@ function conf_path() {
}
$confdir = 'sites';
$uri = explode('/', $_SERVER['PHP_SELF'] ? $_SERVER['PHP_SELF'] : $_SERVER['SCRIPT_FILENAME']);
$uri = explode('/', $_SERVER['SCRIPT_NAME'] ? $_SERVER['SCRIPT_NAME'] : $_SERVER['SCRIPT_FILENAME']);
$server = explode('.', implode('.', array_reverse(explode(':', rtrim($_SERVER['HTTP_HOST'], '.')))));
for ($i = count($uri) - 1; $i > 0; $i--) {
for ($j = count($server); $j > 0; $j--) {
......@@ -255,8 +255,14 @@ function conf_init() {
else {
// Create base URL
$base_root = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') ? 'https' : 'http';
$base_url = $base_root .= '://'. $_SERVER['HTTP_HOST'];
if ($dir = trim(dirname($_SERVER['PHP_SELF']), '\,/')) {
// As $_SERVER['HTTP_HOST'] is user input, ensure it only contains
// characters allowed in hostnames.
$base_url = $base_root .= '://'. preg_replace('/[^a-z0-9-:._]/i', '', $_SERVER['HTTP_HOST']);
// $_SERVER['SCRIPT_NAME'] can, in contrast to $_SERVER['PHP_SELF'], not
// be modified by a visitor.
if ($dir = trim(dirname($_SERVER['SCRIPT_NAME']), '\,/')) {
$base_path = "/$dir";
$base_url .= $base_path;
$base_path .= '/';
......@@ -279,8 +285,9 @@ function conf_init() {
// to use the same session identifiers across http and https.
list( , $session_name) = explode('://', $base_url, 2);
// We try to set the cookie domain to the hostname.
// We escape the hostname because it can be modified by a visitor.
if (!empty($_SERVER['HTTP_HOST'])) {
$cookie_domain = $_SERVER['HTTP_HOST'];
$cookie_domain = check_plain($_SERVER['HTTP_HOST']);
}
}
// Strip leading periods, www., and port numbers from cookie domain.
......@@ -635,10 +642,10 @@ function request_uri() {
}
else {
if (isset($_SERVER['argv'])) {
$uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['argv'][0];
$uri = $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['argv'][0];
}
else {
$uri = $_SERVER['PHP_SELF'] .'?'. $_SERVER['QUERY_STRING'];
$uri = $_SERVER['SCRIPT_NAME'] .'?'. $_SERVER['QUERY_STRING'];
}
}
......
......@@ -548,7 +548,7 @@ function error_handler($errno, $message, $filename, $line) {
$entry = $types[$errno] .': '. $message .' in '. $filename .' on line '. $line .'.';
// Force display of error messages in update.php
if (variable_get('error_level', 1) == 1 || strstr($_SERVER['PHP_SELF'], 'update.php')) {
if (variable_get('error_level', 1) == 1 || strstr($_SERVER['SCRIPT_NAME'], 'update.php')) {
drupal_set_message($entry, 'error');
}
......
......@@ -248,7 +248,7 @@ function drupal_process_form($form_id, &$form) {
$form_button_counter = array(0, 0);
drupal_prepare_form($form_id, $form);
if (($form['#programmed']) || (!empty($_POST) && (($_POST['form_id'] == $form_id) || ($_POST['form_id'] == $form['#base'])))) {
if (($form['#programmed']) || (!empty($_POST) && (($_POST['form_id'] == $form_id)))) {
drupal_validate_form($form_id, $form);
// IE does not send a button value when there is only one submit button (and no non-submit buttons)
// and you submit by pressing enter.
......
......@@ -56,8 +56,9 @@ function sess_read($key) {
function sess_write($key, $value) {
global $user;
// If the client doesn't have a session, and one isn't being created ($value), do nothing.
if (empty($_COOKIE[session_name()]) && empty($value)) {
// If saving of session data is disabled or if the client doesn't have a session,
// and one isn't being created ($value), do nothing.
if (!session_save_session() || (empty($_COOKIE[session_name()]) && empty($value))) {
return TRUE;
}
......@@ -152,3 +153,23 @@ function sess_gc($lifetime) {
return TRUE;
}
/**
* Determine whether to save session data of the current request.
*
* This function allows the caller to temporarily disable writing of session data,
* should the request end while performing potentially dangerous operations, such as
* manipulating the global $user object.
*
* @param $status
* Disables writing of session data when FALSE, (re-)enables writing when TRUE.
*
* @return FALSE if writing session data has been disabled. Otherwise, TRUE.
*/
function session_save_session($status = NULL) {
static $save_session = TRUE;
if (isset($status)) {
$save_session = $status;
}
return ($save_session);
}
......@@ -537,7 +537,7 @@ function blogapi_blogger_title(&$contents) {
}
function blogapi_admin_settings() {
$node_types = node_get_types('names');
$node_types = array_map('check_plain', node_get_types('names'));
$defaults = isset($node_types['blog']) ? array('blog' => 1) : array();
$form['blogapi_node_types'] = array(
'#type' => 'checkboxes',
......
......@@ -1075,23 +1075,8 @@ function comment_delete($cid = NULL) {
$output = '';
// We'll only delete if the user has confirmed the
// deletion using the form in our else clause below.
if (is_object($comment) && is_numeric($comment->cid) && $_POST['confirm']) {
drupal_set_message(t('The comment and all its replies have been deleted.'));
// Delete comment and its replies.
_comment_delete_thread($comment);
_comment_update_node_statistics($comment->nid);
// Clear the cache so an anonymous user sees that his comment was deleted.
cache_clear_all();
drupal_goto("node/$comment->nid");
}
else if (is_object($comment) && is_numeric($comment->cid)) {
$output = drupal_get_form('comment_confirm_delete', $comment->subject, $comment->nid);
if (is_object($comment) && is_numeric($comment->cid)) {
$output = drupal_get_form('comment_confirm_delete', $comment);
}
else {
drupal_set_message(t('The comment no longer exists.'));
......@@ -1100,16 +1085,38 @@ function comment_delete($cid = NULL) {
return $output;
}
function comment_confirm_delete($subject, $nid) {
function comment_confirm_delete($comment) {
$form = array();
$form['comment'] = array(
'#type' => 'value',
'#value' => $comment,
);
return confirm_form(
array(),
t('Are you sure you want to delete the comment %title?', array('%title' => $subject)),
'node/'. $nid,
$form,
t('Are you sure you want to delete the comment %title?', array('%title' => $comment->subject)),
'node/'. $comment->nid,
t('Any replies to this comment will be lost. This action cannot be undone.'),
t('Delete'),
t('Cancel'));
}
function comment_confirm_delete_submit($form_id, $form_values) {
$comment = $form_values['comment'];
// Delete comment and its replies.
_comment_delete_thread($comment);
_comment_update_node_statistics($comment->nid);
// Clear the cache so an anonymous user sees that his comment was deleted.
cache_clear_all();
drupal_set_message(t('The comment and all its replies have been deleted.'));
return "node/$comment->nid";
}
/**
* Comment operations. We offer different update operations depending on
* which comment administration page we're on.
......
......@@ -68,7 +68,8 @@ function menu_menu($may_cache) {
'type' => MENU_CALLBACK);
$items[] = array('path' => 'admin/build/menu/item/disable',
'title' => t('Disable menu item'),
'callback' => 'menu_disable_item',
'callback' => 'drupal_get_form',
'callback arguments' => array('menu_confirm_disable_item'),
'access' => user_access('administer menu'),
'type' => MENU_CALLBACK);
$items[] = array('path' => 'admin/build/menu/item/delete',
......@@ -481,14 +482,24 @@ function menu_reset_item_submit($form_id, $form_values) {
/**
* Menu callback; hide a menu item.
*
* Presents a confirmation form to protect against cross site request forgeries.
*/
function menu_disable_item($mid) {
function menu_confirm_disable_item($mid, $token = NULL) {
global $user;
$item = menu_get_item($mid);
$type = $item['type'];
$form = array();
$form['mid'] = array('#type' => 'value', '#value' => $mid);
$form['item'] = array('#type' => 'value', '#value' => $item);
return confirm_form($form, t('Are you sure you want to disable the menu item %menu-item?', array('%menu-item' => $item['title'])), 'admin/build/menu', ' ', t('Disable'), t('Cancel'));
}
function menu_confirm_disable_item_submit($form_id, $form_values) {
$type = $form_values['item']['type'];
$type &= ~MENU_VISIBLE_IN_TREE;
$type &= ~MENU_VISIBLE_IN_BREADCRUMB;
$type |= MENU_MODIFIED_BY_ADMIN;
db_query('UPDATE {menu} SET type = %d WHERE mid = %d', $type, $mid);
db_query('UPDATE {menu} SET type = %d WHERE mid = %d', $type, $form_values['mid']);
drupal_set_message(t('The menu item has been disabled.'));
drupal_goto('admin/build/menu');
}
......
......@@ -18,7 +18,6 @@ function node_overview_types() {
foreach ($names as $key => $name) {
$type = $types[$key];
if (function_exists($type->module .'_form')) {
$name = check_plain($name);
$type_url_str = str_replace('_', '-', $type->type);
// Populate the operations field.
$operations = array();
......
......@@ -35,7 +35,7 @@ function node_help($section) {
return '<p>'. t('To create a new content type, enter the human-readable name, the machine-readable name, and all other relevant fields that are on this page. Once created, users of your site will be able to create posts that are instances of this content type.') .'</p>';
}
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'revisions') {
if (arg(0) == 'node' && is_numeric(arg(1)) && arg(2) == 'revisions' && arg(3) == NULL) {
return '<p>'. t('The revisions let you track differences between multiple versions of a post.') .'</p>';
}
......@@ -1159,11 +1159,10 @@ function node_menu($may_cache) {
foreach (node_get_types() as $type) {
if (function_exists($type->module .'_form')) {
$name = check_plain($type->name);
$type_url_str = str_replace('_', '-', $type->type);
$items[] = array(
'path' => 'node/add/'. $type_url_str,
'title' => drupal_ucfirst($name),
'title' => drupal_ucfirst($type->name),
'access' => node_access('create', $type->type),
);
}
......@@ -1212,11 +1211,26 @@ function node_menu($may_cache) {
'weight' => 1,
'type' => MENU_CALLBACK);
$revisions_access = ((user_access('view revisions') || user_access('administer nodes')) && node_access('view', $node) && db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', arg(1))) > 1);
$items[] = array('path' => 'node/'. arg(1) .'/revisions', 'title' => t('Revisions'),
$items[] = array(
'path' => 'node/'. arg(1) .'/revisions',
'title' => t('Revisions'),
'callback' => 'node_revisions',
'access' => $revisions_access,
'weight' => 2,
'type' => MENU_LOCAL_TASK);
'type' => MENU_LOCAL_TASK,
);
if (!is_null(arg(3))) {
$items[] = array(
'path' => 'node/'. arg(1) .'/revisions/'. arg(3) .'/delete',
'callback' => 'node_revision_delete',
'callback arguments' => array(arg(1), arg(3)),
);
$items[] = array(
'path' => 'node/'. arg(1) .'/revisions/'. arg(3) .'/revert',
'callback' => 'node_revision_revert',
'callback arguments' => array(arg(1), arg(3)),
);
}
}
}
......@@ -1230,7 +1244,6 @@ function node_menu($may_cache) {
$type = node_get_types('type', $type_name);
if (!empty($type)) {
$type->name = check_plain($type->name);
$type_url_str = str_replace('_', '-', $type->type);
$items[] = array(
......@@ -1569,7 +1582,7 @@ function node_admin_nodes() {
while ($node = db_fetch_object($result)) {
$nodes[$node->nid] = '';
$form['title'][$node->nid] = array('#value' => l($node->title, 'node/'. $node->nid) .' '. theme('mark', node_mark($node->nid, $node->changed)));
$form['name'][$node->nid] = array('#value' => node_get_types('name', $node));
$form['name'][$node->nid] = array('#value' => check_plain(node_get_types('name', $node)));
$form['username'][$node->nid] = array('#value' => theme('username', $node));
$form['status'][$node->nid] = array('#value' => ($node->status ? t('published') : t('not published')));
$form['operations'][$node->nid] = array('#value' => l(t('edit'), 'node/'. $node->nid .'/edit', array(), $destination));
......@@ -1696,16 +1709,7 @@ function node_revision_revert($nid, $revision) {
$node = node_load($nid, $revision);
if ((user_access('revert revisions') || user_access('administer nodes')) && node_access('update', $node)) {
if ($node->vid) {
$node->revision = 1;
$node->log = t('Copy of the revision from %date.', array('%date' => format_date($node->revision_timestamp)));
if (module_exists('taxonomy')) {
$node->taxonomy = array_keys($node->taxonomy);
}
node_save($node);
drupal_set_message(t('%title has been reverted back to the revision from %revision-date', array('%revision-date' => format_date($node->revision_timestamp), '%title' => $node->title)));
watchdog('content', t('@type: reverted %title revision %revision.', array('@type' => t($node->type), '%title' => $node->title, '%revision' => $revision)));
return drupal_get_form('node_revision_revert_confirm', $node);
}
else {
drupal_set_message(t('You tried to revert to an invalid revision.'), 'error');
......@@ -1715,6 +1719,31 @@ function node_revision_revert($nid, $revision) {
drupal_access_denied();
}
/**
* Ask for confirmation of the reversion to prevent against CSRF attacks.
*/
function node_revision_revert_confirm($node) {
$form['node'] = array('#type' => 'value', '#value' => $node);
return confirm_form($form, t('Are you sure you want to revert to the revision from %revision-date?', array('%revision-date' => format_date($node->revision_timestamp))), 'node/'. $node->nid .'/revisions', '', t('Revert'), t('Cancel'));
}
function node_revision_revert_confirm_submit($form_id, $form_values) {
$node = $form_values['node'];
$node->revision = 1;
$node->log = t('Copy of the revision from %date.', array('%date' => format_date($node->revision_timestamp)));
if (module_exists('taxonomy')) {
$node->taxonomy = array_keys($node->taxonomy);
}
node_save($node);
drupal_set_message(t('%title has been reverted back to the revision from %revision-date', array('%revision-date' => format_date($node->revision_timestamp), '%title' => $node->title)));
watchdog('content', t('@type: reverted %title revision %revision.', array('@type' => t($node->type), '%title' => $node->title, '%revision' => $revision)));
return 'node/'. $node->nid .'/revisions';
}
/**
* Delete the revision with specified revision number. A "delete revision" nodeapi event is invoked when a
* revision is deleted.
......@@ -1725,22 +1754,22 @@ function node_revision_delete($nid, $revision) {
if (node_access('delete', $node)) {
// Don't delete the current revision
if ($revision != $node->vid) {
$node = node_load($nid, $revision);
db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $nid, $revision);
node_invoke_nodeapi($node, 'delete revision');
drupal_set_message(t('Deleted %title revision %revision.', array('%title' => $node->title, '%revision' => $revision)));
watchdog('content', t('@type: deleted %title revision %revision.', array('@type' => t($node->type), '%title' => $node->title, '%revision' => $revision)));
if ($node = node_load($nid, $revision)) {
return drupal_get_form('node_revision_delete_confirm', $node);
}
else {
drupal_set_message(t('Deletion failed. You tried to delete a non-existing revision.'));
}
}
else {
drupal_set_message(t('Deletion failed. You tried to delete the current revision.'));
}
if (db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', $nid)) > 1) {
drupal_goto("node/$nid/revisions");
drupal_goto('node/'. $nid .'/revisions');
}
else {
drupal_goto("node/$nid");
drupal_goto('node/'. $nid);
}
}
}
......@@ -1748,6 +1777,29 @@ function node_revision_delete($nid, $revision) {
drupal_access_denied();
}
/**
* Ask confirmation for revision deletion to prevent against CSRF attacks.
*/
function node_revision_delete_confirm($node) {
$form['node'] = array('#type' => 'value', '#value' => $node);
return confirm_form($form, t('Are you sure you want to delete the revision from %revision-date?', array('%revision-date' => format_date($node->revision_timestamp))), 'node/'. $node->nid .'/revisions', t('This action cannot be undone.'), t('Delete'), t('Cancel'));
}
function node_revision_delete_confirm_submit($form_id, $form_values) {
$node = $form_values['node'];
db_query("DELETE FROM {node_revisions} WHERE nid = %d AND vid = %d", $node->nid, $node->vid);
node_invoke_nodeapi($node, 'delete revision');
drupal_set_message(t('Deleted %title revision %revision.', array('%title' => $node->title, '%revision' => $node->vid)));
watchdog('content', t('@type: deleted %title revision %revision.', array('@type' => t($node->type), '%title' => $node->title, '%revision' => $node->vid)));
if (db_result(db_query('SELECT COUNT(vid) FROM {node_revisions} WHERE nid = %d', $node->nid)) > 1) {
return 'node/'. $node->nid .'/revisions';
}
else {
return 'node/'. $node->nid;
}
}
/**
* Return a list of all the existing revision numbers.
*/
......@@ -2352,12 +2404,6 @@ function node_revisions() {
}
}
break;
case 'revert':
node_revision_revert(arg(1), arg(3));
break;
case 'delete':
node_revision_delete(arg(1), arg(3));
break;
}
}
drupal_not_found();
......@@ -2536,7 +2582,7 @@ function node_form_alter($form_id, &$form) {
}
// Node types:
$types = node_get_types('names');
$types = array_map('check_plain', node_get_types('names'));
$form['advanced']['type'] = array(
'#type' => 'checkboxes',
'#title' => t('Only of the type(s)'),
......
......@@ -354,7 +354,9 @@ function poll_teaser($node) {
$teaser = NULL;
if (is_array($node->choice)) {
foreach ($node->choice as $k => $choice) {
$teaser .= '* '. $choice['chtext'] .'\n';
if ($choice['chtext'] != '') {
$teaser .= '* '. check_plain($choice['chtext']) ."\n";
}
}
}
return $teaser;
......
......@@ -111,7 +111,7 @@ function profile_block($op = 'list', $delta = 0, $edit = array()) {
$fields = array();
$result = db_query('SELECT name, title, weight, visibility FROM {profile_fields} WHERE visibility IN (%d, %d) ORDER BY weight', PROFILE_PUBLIC, PROFILE_PUBLIC_LISTINGS);
while ($record = db_fetch_object($result)) {
$fields[$record->name] = $record->title;
$fields[$record->name] = check_plain($record->title);
}
$fields['user_profile'] = t('Link to full user profile');
$form['profile_block_author_fields'] = array('#type' => 'checkboxes',
......@@ -399,7 +399,7 @@ function profile_admin_overview() {
$result = db_query('SELECT title, name, type, category, fid FROM {profile_fields} ORDER BY category, weight');
$rows = array();
while ($field = db_fetch_object($result)) {
$rows[] = array(check_plain($field->title), $field->name, _profile_field_types($field->type), $field->category, l(t('edit'), "admin/user/profile/edit/$field->fid"), l(t('delete'), "admin/user/profile/delete/$field->fid"));
$rows[] = array(check_plain($field->title), check_plain($field->name), _profile_field_types($field->type), check_plain($field->category), l(t('edit'), "admin/user/profile/edit/$field->fid"), l(t('delete'), "admin/user/profile/delete/$field->fid"));
}
if (count($rows) == 0) {
$rows[] = array(array('data' => t('No fields defined.'), 'colspan' => '6'));
......@@ -628,7 +628,7 @@ function profile_form_profile($edit, $user, $category, $register = FALSE) {
while ($field = db_fetch_object($result)) {
$category = $field->category;
if (!isset($fields[$category])) {
$fields[$category] = array('#type' => 'fieldset', '#title' => $category, '#weight' => $w++);
$fields[$category] = array('#type' => 'fieldset', '#title' => check_plain($category), '#weight' => $w++);
}
switch ($field->type) {
case 'textfield':
......@@ -759,7 +759,7 @@ function theme_profile_block($account, $fields = array()) {
$output .= "<p>$field->value</p>\n";
}
else {
$output .= "<p><strong>$field->title</strong><br />$field->value</p>\n";
$output .= '<p><strong>'. check_plain($field->title) ."</strong><br />$field->value</p>\n";
}
}
}
......
......@@ -1233,7 +1233,7 @@ function theme_search_item($item, $type) {
$output = ' <dt class="title"><a href="'. check_url($item['link']) .'">'. check_plain($item['title']) .'</a></dt>';
$info = array();
if ($item['type']) {
$info[] = $item['type'];
$info[] = check_plain($item['type']);
}
if ($item['user']) {
$info[] = $item['user'];
......
......@@ -6,7 +6,7 @@
* Configuration system that lets administrators modify the workings of the site.
*/
define('VERSION', '5.2 dev');
define('VERSION', '5.2');
/**
* Implementation of hook_help().
......@@ -2011,7 +2011,7 @@ function system_theme_settings($key = '') {
'#suffix' => '</div>',
);
foreach ($node_types as $type => $name) {
$form['node_info']["toggle_node_info_$type"] = array('#type' => 'checkbox', '#title' => $name, '#default_value' => $settings["toggle_node_info_$type"]);
$form['node_info']["toggle_node_info_$type"] = array('#type' => 'checkbox', '#title' => check_plain($name), '#default_value' => $settings["toggle_node_info_$type"]);
}
}
}
......
......@@ -152,7 +152,7 @@ function taxonomy_overview_vocabularies() {
$types = array();
foreach ($vocabulary->nodes as $type) {
$node_type = node_get_types('name', $type);
$types[] = $node_type ? $node_type : $type;
$types[] = $node_type ? check_plain($node_type) : check_plain($type);
}
$rows[] = array('name' => check_plain($vocabulary->name),
'type' => implode(', ', $types),
......@@ -251,7 +251,7 @@ function taxonomy_form_vocabulary($edit = array()) {
$form['nodes'] = array('#type' => 'checkboxes',
'#title' => t('Types'),
'#default_value' => $edit['nodes'],
'#options' => node_get_types('names'),
'#options' => array_map('check_plain', node_get_types('names')),
'#description' => t('A list of node types you want to associate with this vocabulary.'),
'#required' => TRUE,
);
......
......@@ -106,7 +106,7 @@ function tracker_page($uid = 0) {
}
$rows[] = array(
node_get_types('name', $node->type),
check_plain(node_get_types('name', $node->type)),
l($node->title, "node/$node->nid") .' '. theme('mark', node_mark($node->nid, $node->changed)),
theme('username', $node),
array('class' => 'replies', 'data' => $comments),
......
......@@ -656,7 +656,7 @@ function theme_user_profile($account, $fields) {
$output .= theme('user_picture', $account);
foreach ($fields as $category => $items) {
if (strlen($category) > 0) {
$output .= '<h2 class="title">'. $category .'</h2>';
$output .= '<h2 class="title">'. check_plain($category) .'</h2>';
}
$output .= '<dl>';
foreach ($items as $item) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment